Make loop, loopz, loopnz and jcxz custom generated

This commit is contained in:
Fabian 2020-12-31 19:14:30 -06:00
parent ea0cd01207
commit a73988a817
9 changed files with 204 additions and 41 deletions

View file

@ -284,9 +284,13 @@ function gen_instruction_body_after_fixed_g(encoding, size)
if(encoding.conditional_jump)
{
console.assert((encoding.opcode & ~0xF) === 0x70 || (encoding.opcode & ~0xF) === 0x0F80);
const condition_index = encoding.opcode & 0xF;
body.push(`analysis.ty = ::analysis::AnalysisType::Jump { offset: jump_offset as i32, condition: Some(${condition_index}), is_32: cpu.osize_32() };`);
console.assert(
(encoding.opcode & ~0xF) === 0x70 ||
(encoding.opcode & ~0xF) === 0x0F80 ||
(encoding.opcode & ~0x3) === 0xE0
);
const condition_index = encoding.opcode & 0xFF;
body.push(`analysis.ty = ::analysis::AnalysisType::Jump { offset: jump_offset as i32, condition: Some(0x${hex(condition_index, 2)}), is_32: cpu.osize_32() };`);
}
else
{

View file

@ -345,12 +345,10 @@ const encodings = [
{ opcode: 0xDF, e: 1, fixed_g: 7, custom: 1, is_fpu: 1, task_switch_test: 1, },
// loop, jcxz, etc.
// Conditional jumps, but condition code not supported by code generator
// (these are never generated by modern compilers)
{ opcode: 0xE0, os: 1, imm8s: 1, skip: 1, block_boundary: 1, /* jump_offset_imm: 1, conditional_jump: 1, */ },
{ opcode: 0xE1, os: 1, imm8s: 1, skip: 1, block_boundary: 1, /* jump_offset_imm: 1, conditional_jump: 1, */ },
{ opcode: 0xE2, os: 1, imm8s: 1, skip: 1, block_boundary: 1, /* jump_offset_imm: 1, conditional_jump: 1, */ },
{ opcode: 0xE3, os: 1, imm8s: 1, skip: 1, block_boundary: 1, /* jump_offset_imm: 1, conditional_jump: 1, */ },
{ opcode: 0xE0, os: 1, imm8s: 1, no_block_boundary_in_interpreted: 1, skip: 1, block_boundary: 1, jump_offset_imm: 1, custom: 1, conditional_jump: 1, },
{ opcode: 0xE1, os: 1, imm8s: 1, no_block_boundary_in_interpreted: 1, skip: 1, block_boundary: 1, jump_offset_imm: 1, custom: 1, conditional_jump: 1, },
{ opcode: 0xE2, os: 1, imm8s: 1, no_block_boundary_in_interpreted: 1, skip: 1, block_boundary: 1, jump_offset_imm: 1, custom: 1, conditional_jump: 1, },
{ opcode: 0xE3, os: 1, imm8s: 1, no_block_boundary_in_interpreted: 1, skip: 1, block_boundary: 1, jump_offset_imm: 1, custom: 1, conditional_jump: 1, },
// port functions aren't jumps, but they may modify eip due to how they are implemented
{ opcode: 0xE4, block_boundary: 1, imm8: 1, skip: 1, }, // in

View file

@ -1601,6 +1601,49 @@ pub fn gen_test_be(builder: &mut WasmBuilder) {
builder.instruction_body.or_i32();
}
pub fn gen_test_loopnz(ctx: &mut JitContext, is_asize_32: bool) {
gen_test_loop(ctx, is_asize_32);
ctx.builder.instruction_body.eqz_i32();
gen_getzf(&mut ctx.builder);
ctx.builder.instruction_body.or_i32();
ctx.builder.instruction_body.eqz_i32();
}
pub fn gen_test_loopz(ctx: &mut JitContext, is_asize_32: bool) {
gen_test_loop(ctx, is_asize_32);
ctx.builder.instruction_body.eqz_i32();
gen_getzf(&mut ctx.builder);
ctx.builder.instruction_body.eqz_i32();
ctx.builder.instruction_body.or_i32();
ctx.builder.instruction_body.eqz_i32();
}
pub fn gen_test_loop(ctx: &mut JitContext, is_asize_32: bool) {
if is_asize_32 {
gen_get_reg32(ctx, regs::ECX);
}
else {
gen_get_reg16(ctx, regs::CX);
}
ctx.builder.instruction_body.const_i32(1);
ctx.builder.instruction_body.sub_i32();
if is_asize_32 {
gen_set_reg32(ctx, regs::ECX);
gen_get_reg32(ctx, regs::ECX);
}
else {
gen_set_reg16(ctx, regs::ECX);
gen_get_reg16(ctx, regs::CX);
}
}
pub fn gen_test_jcxz(ctx: &mut JitContext, is_asize_32: bool) {
if is_asize_32 {
gen_get_reg32(ctx, regs::ECX);
}
else {
gen_get_reg16(ctx, regs::CX);
}
ctx.builder.instruction_body.eqz_i32();
}
pub fn gen_fpu_get_sti(ctx: &mut JitContext, i: u32) {
ctx.builder.instruction_body.const_i32(i as i32);
gen_call_fn1_ret_f64(ctx.builder, "fpu_get_sti");
@ -1641,32 +1684,50 @@ pub fn gen_trigger_gp(ctx: &mut JitContext, error_code: u32) {
ctx.builder.instruction_body.return_();
}
pub fn gen_condition_fn(builder: &mut WasmBuilder, condition: u8) {
dbg_assert!(condition < 16);
if condition == 2 {
gen_getcf(builder);
}
else if condition == 3 {
gen_getcf(builder);
builder.instruction_body.eqz_i32();
}
else if condition == 4 {
gen_getzf(builder);
}
else if condition == 5 {
gen_getzf(builder);
builder.instruction_body.eqz_i32();
}
else if condition == 6 {
gen_test_be(builder);
}
else if condition == 7 {
gen_test_be(builder);
builder.instruction_body.eqz_i32();
pub fn gen_condition_fn(ctx: &mut JitContext, mut condition: u8) {
if condition & 0xF0 == 0x00 || condition & 0xF0 == 0x70 || condition & 0xF0 == 0x80 {
condition &= 0xF;
if condition == 2 {
gen_getcf(ctx.builder);
}
else if condition == 3 {
gen_getcf(ctx.builder);
ctx.builder.instruction_body.eqz_i32();
}
else if condition == 4 {
gen_getzf(ctx.builder);
}
else if condition == 5 {
gen_getzf(ctx.builder);
ctx.builder.instruction_body.eqz_i32();
}
else if condition == 6 {
gen_test_be(ctx.builder);
}
else if condition == 7 {
gen_test_be(ctx.builder);
ctx.builder.instruction_body.eqz_i32();
}
else {
let condition_name = CONDITION_FUNCTIONS[condition as usize];
gen_fn0_const_ret(ctx.builder, condition_name);
}
}
else {
let condition_name = CONDITION_FUNCTIONS[condition as usize];
gen_fn0_const_ret(builder, condition_name);
// loop, loopnz, loopz, jcxz
dbg_assert!(condition & !0x3 == 0xE0);
if condition == 0xE0 {
gen_test_loopnz(ctx, ctx.cpu.asize_32());
}
else if condition == 0xE1 {
gen_test_loopz(ctx, ctx.cpu.asize_32());
}
else if condition == 0xE2 {
gen_test_loop(ctx, ctx.cpu.asize_32());
}
else if condition == 0xE3 {
gen_test_jcxz(ctx, ctx.cpu.asize_32());
}
}
}

View file

@ -1106,9 +1106,9 @@ fn jit_generate_module(
jump_offset_is_32,
} => {
// Conditional jump to next basic block
// - jnz, jc, etc.
// - jnz, jc, loop, jcxz, etc.
codegen::gen_condition_fn(ctx.builder, condition);
codegen::gen_condition_fn(ctx, condition);
ctx.builder.instruction_body.if_void();
// Branch taken

View file

@ -1526,6 +1526,15 @@ pub fn instr32_7E_jit(_ctx: &mut JitContext, _imm: u32) {}
pub fn instr16_7F_jit(_ctx: &mut JitContext, _imm: u32) {}
pub fn instr32_7F_jit(_ctx: &mut JitContext, _imm: u32) {}
pub fn instr16_E0_jit(_ctx: &mut JitContext, _imm: u32) {}
pub fn instr32_E0_jit(_ctx: &mut JitContext, _imm: u32) {}
pub fn instr16_E1_jit(_ctx: &mut JitContext, _imm: u32) {}
pub fn instr32_E1_jit(_ctx: &mut JitContext, _imm: u32) {}
pub fn instr16_E2_jit(_ctx: &mut JitContext, _imm: u32) {}
pub fn instr32_E2_jit(_ctx: &mut JitContext, _imm: u32) {}
pub fn instr16_E3_jit(_ctx: &mut JitContext, _imm: u32) {}
pub fn instr32_E3_jit(_ctx: &mut JitContext, _imm: u32) {}
define_instruction_read_write_mem8!("add8", instr_80_0_mem_jit, instr_80_0_reg_jit, imm8);
define_instruction_read_write_mem8!("or8", instr_80_1_mem_jit, instr_80_1_reg_jit, imm8);
define_instruction_read_write_mem8!("adc8", instr_80_2_mem_jit, instr_80_2_reg_jit, imm8);
@ -3567,7 +3576,7 @@ macro_rules! define_cmovcc16(
pub fn $name_mem(ctx: &mut JitContext, modrm_byte: u8, r: u32) {
codegen::gen_modrm_resolve_safe_read16(ctx, modrm_byte);
let value = ctx.builder.set_new_local();
codegen::gen_condition_fn(ctx.builder, $cond);
codegen::gen_condition_fn(ctx, $cond);
ctx.builder.instruction_body.if_void();
ctx.builder.instruction_body.get_local(&value);
codegen::gen_set_reg16(ctx, r);
@ -3576,7 +3585,7 @@ macro_rules! define_cmovcc16(
}
pub fn $name_reg(ctx: &mut JitContext, r1: u32, r2: u32) {
codegen::gen_condition_fn(ctx.builder, $cond);
codegen::gen_condition_fn(ctx, $cond);
ctx.builder.instruction_body.if_void();
codegen::gen_get_reg16(ctx, r1);
codegen::gen_set_reg16(ctx, r2);
@ -3590,7 +3599,7 @@ macro_rules! define_cmovcc32(
pub fn $name_mem(ctx: &mut JitContext, modrm_byte: u8, r: u32) {
codegen::gen_modrm_resolve_safe_read32(ctx, modrm_byte);
let value = ctx.builder.set_new_local();
codegen::gen_condition_fn(ctx.builder, $cond);
codegen::gen_condition_fn(ctx, $cond);
ctx.builder.instruction_body.if_void();
ctx.builder.instruction_body.get_local(&value);
codegen::gen_set_reg32(ctx, r);
@ -3599,7 +3608,7 @@ macro_rules! define_cmovcc32(
}
pub fn $name_reg(ctx: &mut JitContext, r1: u32, r2: u32) {
codegen::gen_condition_fn(ctx.builder, $cond);
codegen::gen_condition_fn(ctx, $cond);
ctx.builder.instruction_body.if_void();
codegen::gen_get_reg32(ctx, r1);
codegen::gen_set_reg32(ctx, r2);
@ -3649,7 +3658,7 @@ macro_rules! define_setcc(
pub fn $name_mem(ctx: &mut JitContext, modrm_byte: u8, _r: u32) {
codegen::gen_modrm_resolve(ctx, modrm_byte);
let address_local = ctx.builder.set_new_local();
codegen::gen_condition_fn(&mut ctx.builder, $cond);
codegen::gen_condition_fn(ctx, $cond);
ctx.builder.instruction_body.const_i32(0);
ctx.builder.instruction_body.ne_i32();
let value_local = ctx.builder.set_new_local();
@ -3659,7 +3668,7 @@ macro_rules! define_setcc(
}
pub fn $name_reg(ctx: &mut JitContext, r1: u32, _r2: u32) {
codegen::gen_condition_fn(&mut ctx.builder, $cond);
codegen::gen_condition_fn(ctx, $cond);
ctx.builder.instruction_body.const_i32(0);
ctx.builder.instruction_body.ne_i32();
codegen::gen_set_reg8(ctx, r1);

25
tests/nasm/jcxz.asm Normal file
View file

@ -0,0 +1,25 @@
global _start
%include "header.inc"
mov ecx, 0x10000
jecxz cont1
or eax, 1
cont1:
mov ecx, 0
jecxz cont2
or eax, 2
cont2:
mov ecx, 0x1
jcxz cont3
or eax, 4
cont3:
mov ecx, 0x10000
jcxz cont4
or eax, 8
cont4:
%include "footer.inc"

18
tests/nasm/loop.asm Normal file
View file

@ -0,0 +1,18 @@
global _start
%include "header.inc"
mov ecx, 0x10042
mov eax, 0
start1:
inc eax
loop start1
mov ecx, 0x10005
mov ebx, 0
start2:
inc ebx
db 67h
loop start2
%include "footer.inc"

24
tests/nasm/loopnz.asm Normal file
View file

@ -0,0 +1,24 @@
global _start
%include "header.inc"
mov ecx, 0x10042
mov eax, 42
start1:
dec eax
loopz start1
mov ecx, 0x10005
mov ebx, 51
start2:
dec ebx
db 67h
loopz start2
mov ecx, 0x10005
start3:
or edx, 1
db 67h
loopz start3
%include "footer.inc"

24
tests/nasm/loopz.asm Normal file
View file

@ -0,0 +1,24 @@
global _start
%include "header.inc"
mov ecx, 0x10042
mov eax, -1
start1:
inc eax
loopz start1
mov ecx, 0x10005
mov ebx, -1
start2:
inc ebx
db 67h
loopz start2
mov ecx, 0x10005
start3:
xor edx, edx
db 67h
loopz start3
%include "footer.inc"