/* -*-C-*- */ #ifndef _MSC_VER #pragma GCC diagnostic ignored "-Wunused-variable" #endif #define DIE(...) do { MVM_oops(tc, __VA_ARGS__); } while (0) /* NB: The rax/eax/ax/al/ah register is *reserved* for internal use in tiles by * the register allocator. Using rax will never overwrite an allocated value */ /* basic memory traffic tiles */ MVM_JIT_TILE_DECL(addr) { MVMint8 out = tile->values[0]; MVMint8 base = tile->values[1]; MVMint32 ofs = tile->args[0]; | lea Rq(out), [Rq(base)+ofs]; } MVM_JIT_TILE_DECL(idx) { MVMint8 out = tile->values[0]; MVMint8 base = tile->values[1]; MVMint8 idx = tile->values[2]; MVMint8 scl = tile->args[0]; switch(scl) { case 2: | lea Rq(out), [Rq(base)+Rq(idx)*2]; break; case 8: | lea Rq(out), [Rq(base)+Rq(idx)*8]; break; default: DIE("Unsupported scale: %d", scl); } } MVM_JIT_TILE_DECL(const_reg) { MVMint8 out = tile->values[0]; MVMint32 val = tile->args[0]; MVMint32 size = tile->args[1]; | mov Rq(out), val; } MVM_JIT_TILE_DECL(const_large) { MVMint8 out = tile->values[0]; MVMint64 val = tree->constants[tile->args[0]].i; | mov64 Rq(out), val; } MVM_JIT_TILE_DECL(const_num) { MVMuint8 out = tile->values[0] - MVM_JIT_REG(XMM0); MVMnum64 val = tree->constants[tile->args[0]].n; unsigned char bytes[sizeof(val)]; size_t i; memcpy(bytes, &val, sizeof(val)); |.data; |5: for (i = 0; i < sizeof(bytes); i++) { |.byte bytes[i]; } |.code; | movsd xmm(out), qword [<5]; } MVM_JIT_TILE_DECL(load_reg) { MVMint8 out = tile->values[0]; MVMint8 base = tile->values[1]; MVMint32 size = tile->args[0]; switch (size) { case 1: | mov Rb(out), [Rq(base)]; break; case 2: | mov Rw(out), [Rq(base)]; break; case 4: | mov Rd(out), [Rq(base)]; break; case 8: | mov Rq(out), [Rq(base)]; break; default: DIE("Unsupported load size: %d\n", size); } } MVM_JIT_TILE_DECL(load_addr) { MVMint8 out = tile->values[0]; MVMint8 base = tile->values[1]; MVMint32 ofs = tile->args[0]; MVMint32 size = tile->args[1]; switch (tile->args[1]) { case 1: | mov Rb(out), byte [Rq(base)+ofs]; break; case 2: | mov Rw(out), word [Rq(base)+ofs]; break; case 4: | mov Rd(out), dword [Rq(base)+ofs]; break; case 8: | mov Rq(out), qword [Rq(base)+ofs]; break; default: DIE("Unsupported load size: %d\n", size); } } MVM_JIT_TILE_DECL(load_idx) { MVMint8 out = tile->values[0]; MVMint8 base = tile->values[1]; MVMint8 idx = tile->values[2]; MVMint8 scl = tile->args[0]; MVMint32 size = tile->args[1]; if (scl != 8 && size != 2) { DIE("Unsupported scale size: %d\n", scl); } switch (size) { case 1: | mov Rb(out), byte [Rq(base)+Rq(idx)*8]; break; case 2: switch(scl) { case 2: | mov Rw(out), word [Rq(base)+Rq(idx)*2]; break; case 8: | mov Rw(out), word [Rq(base)+Rq(idx)*8]; break; } break; case 4: | mov Rd(out), dword [Rq(base)+Rq(idx)*8]; break; case 8: | mov Rq(out), qword [Rq(base)+Rq(idx)*8]; break; default: DIE("Unsupported load size: %d\n", size); } } MVM_JIT_TILE_DECL(load_num) { MVMint8 out = REG_NUM(tile->values[0]); MVMint8 addr = tile->values[1]; | movsd xmm(out), qword [Rq(addr)]; } MVM_JIT_TILE_DECL(load_num_addr) { MVMint8 out = REG_NUM(tile->values[0]); MVMint8 addr = tile->values[1]; MVMint32 ofs = tile->args[0]; | movsd xmm(out), qword [Rq(addr)+ofs]; } MVM_JIT_TILE_DECL(store) { MVMint8 base = tile->values[1]; MVMint8 value = tile->values[2]; MVMint32 size = tile->args[0]; switch (size) { case 1: | mov byte [Rq(base)], Rb(value); break; case 2: | mov word [Rq(base)], Rw(value); break; case 4: | mov dword [Rq(base)], Rd(value); break; case 8: | mov qword [Rq(base)], Rq(value); break; default: DIE("Unsupported store size: %d\n", size); } } MVM_JIT_TILE_DECL(store_addr) { MVMint8 base = tile->values[1]; MVMint8 value = tile->values[2]; MVMint32 ofs = tile->args[0]; MVMint32 size = tile->args[1]; switch (size) { case 1: | mov byte [Rq(base)+ofs], Rb(value); break; case 2: | mov word [Rq(base)+ofs], Rw(value); break; case 4: | mov dword [Rq(base)+ofs], Rd(value); break; case 8: | mov qword [Rq(base)+ofs], Rq(value); break; default: DIE("Unsupported store size: %d\n", size); } } MVM_JIT_TILE_DECL(store_idx) { MVMint8 base = tile->values[1]; MVMint8 idx = tile->values[2]; MVMint8 scl = tile->args[0]; MVMint32 size = tile->args[1]; MVMint8 value = tile->values[3]; if (scl != 8) DIE("Scale %d NYI\n", scl); switch (size) { case 1: | mov byte [Rq(base)+Rq(idx)*8], Rb(value); break; case 2: | mov word [Rq(base)+Rq(idx)*8], Rw(value); break; case 4: | mov dword [Rq(base)+Rq(idx)*8], Rd(value); break; case 8: | mov qword [Rq(base)+Rq(idx)*8], Rq(value); break; default: DIE("Unsupported store size: %d\n", size); } } MVM_JIT_TILE_DECL(store_num) { MVMint8 addr = tile->values[1]; MVMint8 val = tile->values[2] - MVM_JIT_REG(XMM0); | movsd qword [Rq(addr)], xmm(val); } MVM_JIT_TILE_DECL(store_num_addr) { MVMint8 addr = tile->values[1]; MVMint8 value = REG_NUM(tile->values[2]); MVMint32 ofs = tile->args[0]; | movsd qword [Rq(addr)+ofs], xmm(value); } MVM_JIT_TILE_DECL(cast_signed) { MVMint32 to_size = tile->args[0]; MVMint32 from_size = tile->args[1]; MVMint8 to_reg = tile->values[0]; MVMint8 from_reg = tile->values[1]; /* possible combinations: 1 -> 2, 4, 8; 2 -> 4, 8; 4 -> 8 * Hence we can combine from_size | (to_size << 3) to get * the following options: * 1 + 2<<3 == 17 * 1 + 4<<3 == 33 * 1 + 8<<3 == 65 * 2 + 4<<3 == 34 * 2 + 8<<3 == 66 * 4 + 8<<3 == 68 */ MVMint32 size_conv = (from_size) | (to_size << 3); switch (size_conv) { case 17: | movsx Rw(to_reg), Rb(from_reg); break; case 33: | movsx Rd(to_reg), Rb(from_reg); break; case 34: | movsx Rd(to_reg), Rw(from_reg); break; case 65: | movsx Rq(to_reg), Rb(from_reg); break; case 66: | movsx Rq(to_reg), Rw(from_reg); break; case 68: /* movsx is apparantly not defined for double-to-quadword conversions, * which forces us to use the rax register like it's 1978. It might be easier * to bithack the sign-extension manually, but I'm not sure how.. */ | mov eax, Rd(from_reg); | cdqe; | mov Rq(to_reg), rax; break; default: DIE("Unsupported signed cast %d -> %d\n", from_size, to_size); } } MVM_JIT_TILE_DECL(cast_unsigned) { MVMint32 to_size = tile->args[0]; MVMint32 from_size = tile->args[1]; MVMint8 to_reg = tile->values[0]; MVMint8 from_reg = tile->values[1]; /* Same combination set as above, but we also support casting down: * 8 -> 1 = 8 (1<<3 = 8, 8 | 8 = 8) * 4 -> 1 = 12 * 2 -> 1 = 10 * 8 -> 2 = 24 * 4 -> 2 = 20 * 8 -> 4 = 40 */ MVMint32 size_conv = (from_size) | (to_size << 3); switch (size_conv) { case 8: /* 8 -> 1 */ case 10: /* 2 -> 1 */ case 12: /* 4 -> 1 */ | movzx Rd(to_reg), Rb(from_reg); break; case 17: /* 1 -> 2 */ | movzx Rw(to_reg), Rb(from_reg); break; case 20: /* 4 -> 2 */ case 24: /* 8 -> 2 */ | movzx Rd(to_reg), Rw(to_reg); break; case 33: /* 1 -> 4 */ | movzx Rd(to_reg), Rb(from_reg); break; case 34: /* 2 -> 4 */ | movzx Rd(to_reg), Rw(from_reg); break; case 40: /* 8 -> 4 */ | mov Rd(to_reg), Rd(from_reg); break; case 65: /* 1 -> 8 */ | movzx Rq(to_reg), Rb(from_reg); break; case 66: /* 2 -> 8 */ | movzx Rq(to_reg), Rw(from_reg); break; case 68: /* 4 -> 8 */ /* In contrast to signed cast, nothing special is necessary to cast * unsigned doublewords to quadwords, because using the lower 4 bytes * automatically clears the upper 4 */ | mov Rd(to_reg), Rd(from_reg); break; default: DIE("Unsupported unsigned cast %d -> %d (%d)\n", from_size, to_size, size_conv); } } /* Logic is the same as cast above except loading from memory instead of reg */ /* See comments in cast above for more detail */ MVM_JIT_TILE_DECL(cast_signed_load_addr) { MVMint32 ofs = tile->args[0]; MVMint32 load_size = tile->args[1]; MVMint32 to_size = tile->args[2]; MVMint32 from_size = tile->args[3]; MVMint8 to_reg = tile->values[0]; MVMint8 base = tile->values[1]; MVMint32 size_conv = (from_size) | (to_size << 3); switch (size_conv) { case 17: | movsx Rw(to_reg), byte [Rq(base)+ofs]; break; case 33: | movsx Rd(to_reg), byte [Rq(base)+ofs]; break; case 34: | movsx Rd(to_reg), word [Rq(base)+ofs]; break; case 65: | movsx Rq(to_reg), byte [Rq(base)+ofs]; break; case 66: | movsx Rq(to_reg), word [Rq(base)+ofs]; break; case 68: | mov eax, dword [Rq(base)+ofs]; | cdqe; | mov Rq(to_reg), rax; break; default: DIE("Unsupported signed cast with load %d -> %d\n", from_size, to_size); } } /* Logic is the same as cast above except loading from memory instead of reg */ /* See comments in cast above for more detail */ MVM_JIT_TILE_DECL(cast_unsigned_load_addr) { MVMint32 ofs = tile->args[0]; MVMint32 load_size = tile->args[1]; MVMint32 to_size = tile->args[2]; MVMint32 from_size = tile->args[3]; MVMint8 to_reg = tile->values[0]; MVMint8 base = tile->values[1]; MVMint32 size_conv = (from_size) | (to_size << 3); switch (size_conv) { case 8: /* 8 -> 1 */ case 10: /* 2 -> 1 */ case 12: /* 4 -> 1 */ | movzx Rd(to_reg), byte [Rq(base)+ofs]; break; case 17: /* 1 -> 2 */ | movzx Rw(to_reg), byte [Rq(base)+ofs]; break; case 20: /* 4 -> 2 */ case 24: /* 8 -> 2 */ | movzx Rd(to_reg), word [Rq(base)+ofs]; break; case 33: /* 1 -> 4 */ | movzx Rd(to_reg), byte [Rq(base)+ofs]; break; case 34: /* 2 -> 4 */ | movzx Rd(to_reg), word [Rq(base)+ofs]; break; case 40: /* 8 -> 4 */ | mov Rd(to_reg), dword [Rq(base)+ofs]; break; case 65: /* 1 -> 8 */ | movzx Rq(to_reg), byte [Rq(base)+ofs]; break; case 66: /* 2 -> 8 */ | movzx Rq(to_reg), word [Rq(base)+ofs]; break; case 68: /* 4 -> 8 */ | mov Rd(to_reg), dword [Rq(base)+ofs]; break; default: DIE("Unsupported unsigned cast with load %d -> %d (size_conv=%d)\n", from_size, to_size, size_conv); } } /* binary operations have special requirements because x86 is two-operand form, e.g: * r0 = r0 r1 * whereas the JIT uses a three-operand model: * r0 = r1 r2 */ static void ensure_two_operand_pre(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitTile *tile, MVMint8 reg[2]) { MVMint8 out = tile->values[0], in1 = tile->values[1], in2 = tile->values[2]; assert(out == in1); reg[0] = REG_NUM(out); reg[1] = REG_NUM(in2); } static void ensure_two_operand_post(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitTile *tile, MVMint8 reg[2]) { } MVM_JIT_TILE_DECL(add_reg) { MVMint8 reg[2]; ensure_two_operand_pre(tc, compiler, tile, reg); | add Rq(reg[0]), Rq(reg[1]); ensure_two_operand_post(tc, compiler, tile, reg); } MVM_JIT_TILE_DECL(add_const) { MVMint8 out = tile->values[0]; MVMint8 in1 = tile->values[1]; MVMint32 val = tile->args[0]; MVMint32 sz = tile->args[1]; if (out != in1) { | mov Rq(out), Rq(in1); } | add Rq(out), val; } MVM_JIT_TILE_DECL(add_load_addr) { MVMint8 out = tile->values[0]; MVMint8 in1 = tile->values[1]; MVMint8 base = tile->values[2]; MVMint32 ofs = tile->args[0]; MVMint32 size = tile->args[1]; assert(out == in1); switch (size) { case 1: | add Rb(out), byte [Rq(base)+ofs]; break; case 2: | add Rw(out), word [Rq(base)+ofs]; break; case 4: | add Rd(out), dword [Rq(base)+ofs]; break; case 8: | add Rq(out), qword [Rq(base)+ofs]; break; default: DIE("Unsupported load size: %d\n", size); } } MVM_JIT_TILE_DECL(add_load_idx) { MVMint8 out = tile->values[0]; MVMint8 in1 = tile->values[1]; MVMint8 base = tile->values[2]; MVMint8 idx = tile->values[3]; MVMint32 scl = tile->args[0]; MVMint32 size = tile->args[1]; assert(out == in1); if (scl != 8) DIE("IDX Scale %d NYI\n", scl); switch (size) { case 1: | add Rb(out), byte [Rq(base)+Rq(idx)*8]; break; case 2: | add Rw(out), word [Rq(base)+Rq(idx)*8]; break; case 4: | add Rd(out), dword [Rq(base)+Rq(idx)*8]; break; case 8: | add Rq(out), qword [Rq(base)+Rq(idx)*8]; break; default: DIE("Unsupported load size: %d\n", size); } } MVM_JIT_TILE_DECL(add_num) { MVMint8 reg[2]; ensure_two_operand_pre(tc, compiler, tile, reg); | addsd xmm(reg[0]), xmm(reg[1]); ensure_two_operand_post(tc, compiler, tile, reg); } MVM_JIT_TILE_DECL(sub_num) { MVMint8 reg[2]; ensure_two_operand_pre(tc, compiler, tile, reg); | subsd xmm(reg[0]), xmm(reg[1]); ensure_two_operand_post(tc, compiler, tile, reg); } MVM_JIT_TILE_DECL(mul_num) { MVMint8 reg[2]; ensure_two_operand_pre(tc, compiler, tile, reg); | mulsd xmm(reg[0]), xmm(reg[1]); ensure_two_operand_post(tc, compiler, tile, reg); } MVM_JIT_TILE_DECL(and_reg) { MVMint8 reg[2]; ensure_two_operand_pre(tc, compiler, tile, reg); | and Rq(reg[0]), Rq(reg[1]); ensure_two_operand_post(tc, compiler, tile, reg); } MVM_JIT_TILE_DECL(and_const) { MVMint8 out = tile->values[0]; MVMint8 in1 = tile->values[1]; MVMint32 val = tile->args[0]; MVMint32 sz = tile->args[1]; assert(out == in1); | and Rq(out), val; } MVM_JIT_TILE_DECL(and_load_addr) { MVMint8 out = tile->values[0]; MVMint8 in1 = tile->values[1]; MVMint8 base = tile->values[2]; MVMint32 ofs = tile->args[0]; MVMint32 size = tile->args[1]; assert(out == in1); switch (size) { case 1: | and Rb(out), byte [Rq(base)+ofs]; break; case 2: | and Rw(out), word [Rq(base)+ofs]; break; case 4: | and Rd(out), dword [Rq(base)+ofs]; break; case 8: | and Rq(out), qword [Rq(base)+ofs]; break; default: DIE("Unsupported load size: %d\n", size); } } MVM_JIT_TILE_DECL(and_load_idx) { MVMint8 out = tile->values[0]; MVMint8 in1 = tile->values[1]; MVMint8 base = tile->values[2]; MVMint8 idx = tile->values[3]; MVMint32 scl = tile->args[0]; MVMint32 size = tile->args[1]; assert(out == in1); if (scl != 8) DIE("IDX Scale %d NYI\n", scl); switch (size) { case 1: | and Rb(out), byte [Rq(base)+Rq(idx)*8]; break; case 2: | and Rw(out), word [Rq(base)+Rq(idx)*8]; break; case 4: | and Rd(out), dword [Rq(base)+Rq(idx)*8]; break; case 8: | and Rq(out), qword [Rq(base)+Rq(idx)*8]; break; default: DIE("Unsupported load size: %d\n", size); } } MVM_JIT_TILE_DECL(mul_reg) { MVMint8 reg[2]; ensure_two_operand_pre(tc, compiler, tile, reg); | imul Rq(reg[0]), Rq(reg[1]); ensure_two_operand_post(tc, compiler, tile, reg); } MVM_JIT_TILE_DECL(or_reg) { MVMint8 reg[2]; ensure_two_operand_pre(tc, compiler, tile, reg); | or Rq(reg[0]), Rq(reg[1]); ensure_two_operand_post(tc, compiler, tile, reg); } MVM_JIT_TILE_DECL(xor_reg) { MVMint8 reg[2]; ensure_two_operand_pre(tc, compiler, tile, reg); | xor Rq(reg[0]), Rq(reg[1]); ensure_two_operand_post(tc, compiler, tile, reg); } MVM_JIT_TILE_DECL(not_reg) { MVMint8 out = tile->values[0]; MVMint8 in = tile->values[1]; assert(out == in); | not Rq(out); } MVM_JIT_TILE_DECL(sub_reg) { MVMint8 reg[2]; ensure_two_operand_pre(tc, compiler, tile, reg); | sub Rq(reg[0]), Rq(reg[1]); ensure_two_operand_post(tc, compiler, tile, reg); } MVM_JIT_TILE_DECL(sub_const) { MVMint8 out = tile->values[0]; MVMint8 in1 = tile->values[1]; MVMint32 val = tile->args[0]; MVMint32 sz = tile->args[1]; assert(out == in1); | sub Rq(out), val; } MVM_JIT_TILE_DECL(sub_load_addr) { MVMint8 out = tile->values[0]; MVMint8 in1 = tile->values[1]; MVMint8 base = tile->values[2]; MVMint32 ofs = tile->args[0]; MVMint32 size = tile->args[1]; assert(out == in1); switch (size) { case 1: | sub Rb(out), byte [Rq(base)+ofs]; break; case 2: | sub Rw(out), word [Rq(base)+ofs]; break; case 4: | sub Rd(out), dword [Rq(base)+ofs]; break; case 8: | sub Rq(out), qword [Rq(base)+ofs]; break; default: DIE("Unsupported load size: %d\n", size); } } MVM_JIT_TILE_DECL(sub_load_idx) { MVMint8 out = tile->values[0]; MVMint8 in1 = tile->values[1]; MVMint8 base = tile->values[2]; MVMint8 idx = tile->values[3]; MVMint32 scl = tile->args[0]; MVMint32 size = tile->args[1]; assert(out != in1); if (scl != 8) DIE("IDX Scale %d NYI\n", scl); switch (size) { case 1: | sub Rb(out), byte [Rq(base)+Rq(idx)*8]; break; case 2: | sub Rw(out), word [Rq(base)+Rq(idx)*8]; break; case 4: | sub Rd(out), dword [Rq(base)+Rq(idx)*8]; break; case 8: | sub Rq(out), qword [Rq(base)+Rq(idx)*8]; break; default: DIE("Unsupported load size: %d\n", size); } } MVM_JIT_TILE_DECL(test) { MVMint8 reg = tile->values[1]; switch (tile->size) { case 1: | test Rb(reg), Rb(reg); break; case 2: | test Rw(reg), Rw(reg); break; case 4: | test Rd(reg), Rd(reg); break; case 8: default: /* NB - this is a hack, because we don't assign a size to the result of * CALL, its size would be 0, and we'd have nothing compiled. Better fix * would be to assign a result size to CALL. */ | test Rq(reg), Rq(reg); break; } } MVM_JIT_TILE_DECL(test_addr) { MVMint8 base = tile->values[1]; MVMint32 ofs = tile->args[0]; MVMint32 size = tile->args[1]; /* TODO - I now suspect that this can be OR [addr], 0, without loss of generality */ switch (size) { case 1: | cmp byte [Rq(base)+ofs], 0; break; case 2: | cmp word [Rq(base)+ofs], 0; break; case 4: | cmp dword [Rq(base)+ofs], 0; break; case 8: | cmp qword [Rq(base)+ofs], 0; break; default: DIE("Unsupported size %d for load\n", size); } } MVM_JIT_TILE_DECL(test_idx) { MVMint8 base = tile->values[1]; MVMint8 idx = tile->values[2]; MVMint32 scl = tile->args[0]; MVMint32 size = tile->args[1]; if (scl != 8) DIE("Scale %d NYI\n", scl); switch(size) { case 1: | cmp byte [Rq(base)+Rq(idx)*8], 0; break; case 2: | cmp word [Rq(base)+Rq(idx)*8], 0; break; case 4: | cmp dword [Rq(base)+Rq(idx)*8], 0; break; case 8: | cmp qword [Rq(base)+Rq(idx)*8], 0; break; default: DIE("Unsupported size %d for load\n", tile->size); } } MVM_JIT_TILE_DECL(test_and) { MVMint8 rega = tile->values[1]; MVMint8 regb = tile->values[2]; switch(tile->size) { case 1: | test Rb(regb), Rb(rega); break; case 2: | test Rw(regb), Rw(rega); break; case 4: | test Rd(regb), Rd(rega); break; case 8: | test Rq(regb), Rq(rega); break; } } MVM_JIT_TILE_DECL(test_const) { MVMint8 reg = tile->values[1]; MVMint32 val = tile->args[0]; switch(tile->size) { case 1: | test Rb(reg), val; break; case 2: | test Rw(reg), val; break; case 4: | test Rd(reg), val; break; default: DIE("Unsupported size of constant"); break; } } MVM_JIT_TILE_DECL(test_addr_const) { MVMint8 reg = tile->values[1]; /* args: $ofs $lsize $val $csize */ MVMint32 ofs = tile->args[0]; MVMint32 val = tile->args[2]; switch(tile->size) { case 1: | test byte [Rq(reg)+ofs], val; break; case 2: | test word [Rq(reg)+ofs], val; break; case 4: | test dword [Rq(reg)+ofs], val; break; default: DIE("Unsupported size of constant"); break; } } MVM_JIT_TILE_DECL(test_num) { MVMint8 reg = REG_NUM(tile->values[1]); | xorpd xmm0, xmm0; | ucomisd xmm(reg), xmm0; } MVM_JIT_TILE_DECL(cmp) { MVMint8 regl = tile->values[1]; MVMint8 regr = tile->values[2]; switch (tile->size) { case 1: | cmp Rb(regl), Rb(regr); break; case 2: | cmp Rw(regl), Rw(regr); break; case 4: | cmp Rd(regl), Rd(regr); break; case 8: | cmp Rq(regl), Rq(regr); break; } } MVM_JIT_TILE_DECL(cmp_num) { MVMint8 left = REG_NUM(tile->values[1]); MVMint8 right = REG_NUM(tile->values[2]); | ucomisd xmm(left), xmm(right); } MVM_JIT_TILE_DECL(flagval) { /* Intel SDM Volume 1, ch5.1.6 - Bit and Byte Instructions */ MVMint8 out = tile->values[0]; MVMint32 child = MVM_JIT_EXPR_LINKS(tree, tile->node)[0]; enum MVMJitExprOperator flag = tree->nodes[child]; /* This isn't exactly elegant, better mechanism wanted */ MVMuint8 test_type = MVM_JIT_EXPR_INFO(tree, child)->type; if (test_type == MVM_reg_num32 || test_type == MVM_reg_num64) { switch (flag) { case MVM_JIT_LT: | setnp al; | setb Rb(out); | and Rb(out), al; break; case MVM_JIT_LE: | setnp al; | setbe Rb(out); | and Rb(out), al; break; case MVM_JIT_ZR: case MVM_JIT_EQ: | setnp al; | setz Rb(out); | and Rb(out), al; break; case MVM_JIT_NZ: case MVM_JIT_NE: | setp al; | setnz Rb(out); | or Rb(out), al; break; case MVM_JIT_GE: | setae Rb(out); break; case MVM_JIT_GT: | seta Rb(out); break; default: abort(); } } else { switch (flag) { case MVM_JIT_LT: | setl Rb(out); break; case MVM_JIT_LE: | setle Rb(out); break; case MVM_JIT_ZR: case MVM_JIT_EQ: | setz Rb(out); break; case MVM_JIT_NZ: case MVM_JIT_NE: | setnz Rb(out); break; case MVM_JIT_GE: | setge Rb(out); break; case MVM_JIT_GT: | setg Rb(out); break; default: abort(); } } /* XXX THIS IS A HACK * The size cast is supposed to be applied by the expression template * builder, but that subtly doesn't work (it's not applied for STORE * operands, and when it is, it causes even subtler errors with CONST * arguments. (const_i64_16 returns a 64 bit signed integer as a 16 bit * signed integer argument, and currently CONST doesn't have a sign, and the * tile yielding the value (fortunately) doesn't respect the size. */ | movzx Rq(out), Rb(out); } MVM_JIT_TILE_DECL(mark) { MVMint32 label = tile->args[0]; |=>(label): } MVM_JIT_TILE_DECL(label) { MVMint8 reg = tile->values[0]; MVMint32 label = tile->args[0]; | lea Rq(reg), [=>label]; } MVM_JIT_TILE_DECL(branch_label) { MVMint32 label = tile->args[0]; if (label >= 0) { | jmp =>(label); } else { | jmp ->exit; } } static void move_call_value(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitTile *tile) { if (MVM_JIT_TILE_YIELDS_VALUE(tile)) { MVMint8 out = tile->values[0]; if (IS_GPR(out)) { | mov Rq(out), rax; } else { | movsd xmm(REG_NUM(out)), xmm0; } } } MVM_JIT_TILE_DECL(call) { MVMint8 reg = tile->values[1]; | call Rq(reg); move_call_value(tc, compiler, tile); } MVM_JIT_TILE_DECL(call_func) { uintptr_t ptr = tree->constants[tile->args[0]].u; | callp ptr; move_call_value(tc, compiler, tile); } MVM_JIT_TILE_DECL(call_addr) { MVMint8 reg = tile->values[1]; MVMint32 ofs = tile->args[0]; | call qword [Rq(reg)+ofs]; move_call_value(tc, compiler, tile); }