summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/lib/codegen-x64-test.js
blob: 5d64226bf2d384867f588fdae1ac51096d3dc4b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// Scaffolding for testing x64 Ion code generation patterns).  See
// ../tests/wasm/README-codegen.md for general information, and the *codegen.js
// tests in that directory and its subdirectories for specifics.
//
// The structure of the inputs can vary for the different testing predicates but
// each case generally has an operator name and an expected-pattern; the latter
// is a string that represents a regular expression, possibly with newlines,
// representing the instruction or instructions we are looking for for the
// operator.  Spaces in the expected-pattern are arbitrary, we preprocess the
// pattern to replace any space string with \s+.  Lines are separated by
// newlines and leading and trailing spaces are currently stripped.
//
// The testers additionally take an optional options bag with the following
// optional entries:
//  features: if present, an object to pass as the last argument to functions
//            that compile wasm bytecode
//  instanceBox: if present, an object with a `value` property that will
//               receive the constructed instance
//  no_prefix: by default, the required pattern must be immediately preceded
//             by `x64_prefix`, and this is checked.  Setting this to true skips
//             the check.
//  no_suffix: by default, the required pattern must be immediately followed
//             by `x64_suffix`, and this is checked.  Setting this to true skips
//             the check.
//  memory: if present, add a memory of length given by this property
//  log: for debugging -- print the disassembly, then the preprocessed pattern

load(libdir + "codegen-test-common.js");

// RIP-relative address following the instruction mnemonic
var RIPR = `0x${HEXES}`;

// RIP-relative address in the binary encoding
var RIPRADDR = `${HEX}{2} ${HEX}{2} ${HEX}{2} ${HEX}{2}`;

// End of prologue
var x64_prefix = `48 89 e5                  mov %rsp, %rbp`

// Start of epilogue
var x64_suffix = `5d                        pop %rbp`;

// v128 OP v128 -> v128
// inputs: [[complete-opname, expected-pattern], ...]
function codegenTestX64_v128xv128_v128(inputs, options = {}) {
    for ( let [op, expected] of inputs ) {
        codegenTestX64BinopInternal(op, expected, options, 'v128', 'v128', 'v128', '0', '1');
    }
}

// v128 OP param1-type -> v128
// inputs: [[complete-opname, param1-type, expected-pattern], ...]
function codegenTestX64_v128xPTYPE_v128(inputs, options = {}) {
    for ( let [op, p1type, expected] of inputs ) {
        codegenTestX64BinopInternal(op, expected, options, 'v128', p1type, 'v128', '0', '1');
    }
}

// v128 OP literal -> v128
// inputs: [[complete-opname, rhs-literal, expected-pattern], ...]
function codegenTestX64_v128xLITERAL_v128(inputs, options = {}) {
    for ( let [op, literal, expected] of inputs ) {
        codegenTestX64_adhoc(wrap(options, `
    (func (export "f") (param v128) (result v128)
      (${op} (local.get 0) ${literal}))`),
                              'f',
                              expected,
                              options)
    }
}

// literal OP v128 -> v128
// inputs: [[complete-opname, lhs-literal, expected-pattern], ...]
function codegenTestX64_LITERALxv128_v128(inputs, options = {}) {
    for ( let [op, literal, expected] of inputs ) {
        codegenTestX64_adhoc(wrap(options, `
    (func (export "f") (param v128) (result v128)
      (${op} ${literal} (local.get 0)))`),
                              'f',
                              expected,
                              options)
    }
}

// v128 OP v128 -> v128, but operands are swapped
// inputs: [[complete-opname, expected-pattern], ...]
function codegenTestX64_v128xv128_v128_reversed(inputs, options = {}) {
    for ( let [op, expected] of inputs ) {
        codegenTestX64BinopInternal(op, expected, options, 'v128', 'v128', 'v128', '1', '0');
    }
}

// OP v128 -> v128
// inputs: [[complete-opname, expected-pattern], ...]
function codegenTestX64_v128_v128(inputs, options = {}) {
    for ( let [op, expected] of inputs ) {
        codegenTestX64_adhoc(wrap(options, `
    (func (export "f") (param v128) (result v128)
      (${op} (local.get 0)))`),
                             'f',
                             expected,
                             options);
    }
}

// OP param-type -> v128
// inputs [[complete-opname, param-type, expected-pattern], ...]
function codegenTestX64_PTYPE_v128(inputs, options = {}) {
    for ( let [op, ptype, expected] of inputs ) {
        codegenTestX64_adhoc(wrap(options, `
    (func (export "f") (param ${ptype}) (result v128)
      (${op} (local.get 0)))`),
                             'f',
                             expected,
                             options);
    }
}

// OP v128 -> v128, but the function takes two arguments and the first is ignored
// inputs: [[complete-opname, expected-pattern], ...]
function codegenTestX64_IGNOREDxv128_v128(inputs, options = {}) {
    for ( let [op, expected] of inputs ) {
        codegenTestX64_adhoc(wrap(options, `
    (func (export "f") (param v128) (param v128) (result v128)
      (${op} (local.get 1)))`),
                             'f',
                             expected,
                             options);
    }
}

// () -> v128
// inputs: [[complete-opname, expected-pattern], ...]
function codegenTestX64_unit_v128(inputs, options = {}) {
    for ( let [op, expected] of inputs ) {
        codegenTestX64_adhoc(wrap(options, `
   (func (export "f") (result v128)
     (${op}))`),
                             'f',
                             expected,
                             options);
    }
}

// For when nothing else applies: `module_text` is the complete source text of
// the module, `export_name` is the name of the function to be tested,
// `expected` is the non-preprocessed pattern, and options is an options bag,
// described above.
function codegenTestX64_adhoc(module_text, export_name, expected, options = {}) {
    assertEq(hasDisassembler(), true);

    let ins = wasmEvalText(module_text, {}, options.features);
    if (options.instanceBox)
        options.instanceBox.value = ins;
    let output = wasmDis(ins.exports[export_name], {tier:"ion", asString:true});
    if (!options.no_prefix)
        expected = x64_prefix + '\n' + expected;
    if (!options.no_suffix)
        expected = expected + '\n' + x64_suffix;
    const expected_pretty = striplines(expected);
    expected = fixlines(expected);

    const success = output.match(new RegExp(expected)) != null;
    if (options.log || !success) {
        print("Module text:")
        print(module_text);
        print("Actual output:")
        print(output);
        print("Expected output (easy-to-read and fully-regex'd):")
        print(expected_pretty);
        print(expected);
    }
    assertEq(success, true);
}

// Internal code below this line

function codegenTestX64BinopInternal(op, expected, options, p0type, p1type, restype, arg0, arg1) {
    codegenTestX64_adhoc(wrap(options, `
    (func (export "f") (param ${p0type}) (param ${p1type}) (result ${restype})
      (${op} (local.get ${arg0}) (local.get ${arg1})))`),
                         'f',
                         expected,
                         options);
}