From 090255456aa137f28cc254c609ff95fb7fd3d71b Mon Sep 17 00:00:00 2001 From: Maxime Chevalier-Boisvert Date: Mon, 14 Sep 2020 16:59:39 -0400 Subject: Ported neg, and, or instructions --- ujit_asm.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++-------- ujit_asm.h | 5 +- ujit_asm_tests.c | 26 ++++++---- ujit_compile.c | 41 +++++++++++++--- 4 files changed, 178 insertions(+), 37 deletions(-) diff --git a/ujit_asm.c b/ujit_asm.c index 67e739f8ad..ce0e9fb2fe 100644 --- a/ujit_asm.c +++ b/ujit_asm.c @@ -746,6 +746,22 @@ void cb_write_jcc(codeblock_t* cb, const char* mnem, uint8_t op0, uint8_t op1, s cb_write_int(cb, 0, 32); } +// Encode a conditional move instruction +/* +void writeCmov(CodeBlock cb, const char mnem, ubyte opcode1, X86Reg dst, X86Opnd src) +{ + //cb.writeASM(mnem, dst, src); + + assert (src.isReg || src.isMem); + assert (dst.size >= 16, "invalid dst reg size in cmov"); + + auto szPref = dst.size is 16; + auto rexW = dst.size is 64; + + cb.writeRMInstr!('r', 0xFF, 0x0F, opcode1)(szPref, rexW, X86Opnd(dst), src); +} +*/ + // add - Integer addition void add(codeblock_t* cb, x86opnd_t opnd0, x86opnd_t opnd1) { @@ -765,6 +781,25 @@ void add(codeblock_t* cb, x86opnd_t opnd0, x86opnd_t opnd1) ); } +/// and - Bitwise AND +void and(codeblock_t* cb, x86opnd_t opnd0, x86opnd_t opnd1) +{ + cb_write_rm_multi( + cb, + "and", + 0x20, // opMemReg8 + 0x21, // opMemRegPref + 0x22, // opRegMem8 + 0x23, // opRegMemPref + 0x80, // opMemImm8 + 0x83, // opMemImmSml + 0x81, // opMemImmLrg + 0x04, // opExtImm + opnd0, + opnd1 + ); +} + /// call - Call to label with 32-bit offset void call_label(codeblock_t* cb, size_t label_idx) { @@ -787,6 +822,40 @@ void call(codeblock_t* cb, x86opnd_t opnd) cb_write_rm(cb, false, false, NO_OPND, opnd, 2, 1, 0xFF); } +/* +/// cmovcc - Conditional move +alias cmova = writeCmov!("cmova", 0x47); +alias cmovae = writeCmov!("cmovae", 0x43); +alias cmovb = writeCmov!("cmovb", 0x42); +alias cmovbe = writeCmov!("cmovbe", 0x46); +alias cmovc = writeCmov!("cmovc", 0x42); +alias cmove = writeCmov!("cmove", 0x44); +alias cmovg = writeCmov!("cmovg", 0x4F); +alias cmovge = writeCmov!("cmovge", 0x4D); +alias cmovl = writeCmov!("cmovl", 0x4C); +alias cmovle = writeCmov!("cmovle", 0x4E); +alias cmovna = writeCmov!("cmovna", 0x46); +alias cmovnae = writeCmov!("cmovnae", 0x42); +alias cmovnb = writeCmov!("cmovnb", 0x43); +alias cmovnbe = writeCmov!("cmovnbe", 0x47); +alias cmovnc = writeCmov!("cmovnc", 0x43); +alias cmovne = writeCmov!("cmovne", 0x45); +alias cmovnge = writeCmov!("cmovng", 0x4E); +alias cmovnge = writeCmov!("cmovnge", 0x4C); +alias cmovnl = writeCmov!("cmovnl", 0x4D); +alias cmovnle = writeCmov!("cmovnle", 0x4F); +alias cmovno = writeCmov!("cmovno", 0x41); +alias cmovnp = writeCmov!("cmovnp", 0x4B); +alias cmovns = writeCmov!("cmovns", 0x49); +alias cmovnz = writeCmov!("cmovnz", 0x45); +alias cmovo = writeCmov!("cmovno", 0x40); +alias cmovp = writeCmov!("cmovp", 0x4A); +alias cmovpe = writeCmov!("cmovpe", 0x4A); +alias cmovpo = writeCmov!("cmovpo", 0x4B); +alias cmovs = writeCmov!("cmovs", 0x48); +alias cmovz = writeCmov!("cmovz", 0x44); +*/ + /// cmp - Compare and set flags void cmp(codeblock_t* cb, x86opnd_t opnd0, x86opnd_t opnd1) { @@ -1094,6 +1163,19 @@ void mov(codeblock_t* cb, x86opnd_t dst, x86opnd_t src) } } +// neg - Integer negation (multiplication by -1) +void neg(codeblock_t* cb, x86opnd_t opnd) +{ + write_rm_unary( + cb, + "neg", + 0xF6, // opMemReg8 + 0xF7, // opMemRegPref + 0x03, // opExt + opnd + ); +} + // nop - Noop, one or multiple bytes long void nop(codeblock_t* cb, size_t length) { @@ -1174,45 +1256,49 @@ void not(codeblock_t* cb, x86opnd_t opnd) ); } -/* /// or - Bitwise OR -alias or = writeRMMulti!( - "or", - 0x08, // opMemReg8 - 0x09, // opMemRegPref - 0x0A, // opRegMem8 - 0x0B, // opRegMemPref - 0x80, // opMemImm8 - 0x83, // opMemImmSml - 0x81, // opMemImmLrg - 0x01 // opExtImm -); -*/ +void or(codeblock_t* cb, x86opnd_t opnd0, x86opnd_t opnd1) +{ + cb_write_rm_multi( + cb, + "or", + 0x08, // opMemReg8 + 0x09, // opMemRegPref + 0x0A, // opRegMem8 + 0x0B, // opRegMemPref + 0x80, // opMemImm8 + 0x83, // opMemImmSml + 0x81, // opMemImmLrg + 0x01, // opExtImm + opnd0, + opnd1 + ); +} -/// push - Push a register on the stack -void push(codeblock_t* cb, x86opnd_t reg) +/// pop - Pop a register off the stack +void pop(codeblock_t* cb, x86opnd_t reg) { assert (reg.num_bits == 64); - //cb.writeASM("push", reg); + //cb.writeASM("pop", reg); if (rex_needed(reg)) cb_write_rex(cb, false, 0, 0, reg.reg.reg_no); - cb_write_opcode(cb, 0x50, reg); + cb_write_opcode(cb, 0x58, reg); } -/// pop - Pop a register off the stack -void pop(codeblock_t* cb, x86opnd_t reg) +/// push - Push a register on the stack +void push(codeblock_t* cb, x86opnd_t reg) { assert (reg.num_bits == 64); - //cb.writeASM("pop", reg); + //cb.writeASM("push", reg); if (rex_needed(reg)) cb_write_rex(cb, false, 0, 0, reg.reg.reg_no); - cb_write_opcode(cb, 0x58, reg); + cb_write_opcode(cb, 0x50, reg); } /// ret - Return from call, popping only the return address @@ -1299,3 +1385,18 @@ void sub(codeblock_t* cb, x86opnd_t opnd0, x86opnd_t opnd1) opnd1 ); } + +/* +/// xor - Exclusive bitwise OR +alias xor = writeRMMulti!( + "xor", + 0x30, // opMemReg8 + 0x31, // opMemRegPref + 0x32, // opRegMem8 + 0x33, // opRegMemPref + 0x80, // opMemImm8 + 0x83, // opMemImmSml + 0x81, // opMemImmLrg + 0x06 // opExtImm +); +*/ diff --git a/ujit_asm.h b/ujit_asm.h index af8f0d7fc7..569c310381 100644 --- a/ujit_asm.h +++ b/ujit_asm.h @@ -193,6 +193,7 @@ void cb_link_labels(codeblock_t* cb); // Encode individual instructions into a code block void add(codeblock_t* cb, x86opnd_t opnd0, x86opnd_t opnd1); +void and(codeblock_t* cb, x86opnd_t opnd0, x86opnd_t opnd1); void call_label(codeblock_t* cb, size_t label_idx); void call(codeblock_t* cb, x86opnd_t opnd); void cmp(codeblock_t* cb, x86opnd_t opnd0, x86opnd_t opnd1); @@ -234,10 +235,12 @@ void jmp(codeblock_t* cb, size_t label_idx); void jmp_rm(codeblock_t* cb, x86opnd_t opnd); void lea(codeblock_t* cb, x86opnd_t dst, x86opnd_t src); void mov(codeblock_t* cb, x86opnd_t dst, x86opnd_t src); +void neg(codeblock_t* cb, x86opnd_t opnd); void nop(codeblock_t* cb, size_t length); void not(codeblock_t* cb, x86opnd_t opnd); -void push(codeblock_t* cb, x86opnd_t reg); +void or(codeblock_t* cb, x86opnd_t opnd0, x86opnd_t opnd1); void pop(codeblock_t* cb, x86opnd_t reg); +void push(codeblock_t* cb, x86opnd_t reg); void ret(codeblock_t* cb); void sal(codeblock_t* cb, x86opnd_t opnd0, x86opnd_t opnd1); void sar(codeblock_t* cb, x86opnd_t opnd0, x86opnd_t opnd1); diff --git a/ujit_asm_tests.c b/ujit_asm_tests.c index 4ada3a9ecd..e163404d05 100644 --- a/ujit_asm_tests.c +++ b/ujit_asm_tests.c @@ -30,7 +30,7 @@ void check_bytes(codeblock_t* cb, const char* bytes) if (cb_byte != byte) { - fprintf(stderr, "incorrect encoding at position %ld, got %X, expected %X\n", + fprintf(stderr, "incorrect encoding at position %ld, got %02X, expected %02X\n", i, (int)cb_byte, (int)byte @@ -47,8 +47,6 @@ void run_tests() codeblock_t cb_obj; codeblock_t* cb = &cb_obj; cb_init(cb, 4096); - cb_write_prologue(cb); - cb_write_epilogue(cb); // add /* @@ -82,6 +80,9 @@ void run_tests() cb_set_pos(cb, 0); add(cb, ECX, imm_opnd(8)); check_bytes(cb, "83C108"); cb_set_pos(cb, 0); add(cb, ECX, imm_opnd(255)); check_bytes(cb, "81C1FF000000"); + // and + cb_set_pos(cb, 0); and(cb, EBP, R12D); check_bytes(cb, "4421E5"); + // call { cb_set_pos(cb, 0); @@ -212,6 +213,10 @@ void run_tests() ); */ cb_set_pos(cb, 0); mov(cb, mem_opnd(8, RSP, 0), imm_opnd(-3)); check_bytes(cb, "C60424FD"); + cb_set_pos(cb, 0); mov(cb, mem_opnd(64, RDI, 8), imm_opnd(1)); check_bytes(cb, "48C7470801000000"); + + // neg + cb_set_pos(cb, 0); neg(cb, RAX); check_bytes(cb, "48F7D8"); // nop cb_set_pos(cb, 0); nop(cb, 1); check_bytes(cb, "90"); @@ -295,12 +300,7 @@ void run_tests() */ // or - /* - test( - delegate void (CodeBlock cb) { cb.or(X86Opnd(EDX), X86Opnd(ESI)); }, - "09F2" - ); - */ + cb_set_pos(cb, 0); or(cb, EDX, ESI); check_bytes(cb, "09F2"); // pop cb_set_pos(cb, 0); pop(cb, RAX); check_bytes(cb, "58"); @@ -346,6 +346,14 @@ void run_tests() cb_set_pos(cb, 0); sub(cb, EAX, imm_opnd(1)); check_bytes(cb, "83E801"); cb_set_pos(cb, 0); sub(cb, RAX, imm_opnd(2)); check_bytes(cb, "4883E802"); + /* + // xor + test( + delegate void (CodeBlock cb) { cb.xor(X86Opnd(EAX), X86Opnd(EAX)); }, + "31C0" + ); + */ + printf("Assembler tests done\n"); } diff --git a/ujit_compile.c b/ujit_compile.c index 19b2b35c12..88eb2d657e 100644 --- a/ujit_compile.c +++ b/ujit_compile.c @@ -62,6 +62,11 @@ ujit_compile_insn(rb_iseq_t *iseq, size_t insn_idx) ujit_init(); } + if (cb->write_pos + 1024 >= cb->mem_size) + { + rb_bug("out of executable memory"); + } + int insn = (int)iseq->body->iseq_encoded[insn_idx]; int len = insn_len(insn); //const char* name = insn_name(insn); @@ -74,6 +79,9 @@ ujit_compile_insn(rb_iseq_t *iseq, size_t insn_idx) uint8_t* code_ptr = &cb->mem_block[cb->write_pos]; //printf("write pos: %ld\n", cb->write_pos); + // Write the pre call bytes + ujit_instr_entry(cb); + // TODO: encode individual instructions, eg // nop, putnil, putobject, putself, pop, dup, getlocal, setlocal, nilp @@ -81,9 +89,6 @@ ujit_compile_insn(rb_iseq_t *iseq, size_t insn_idx) // into separate functions if (insn == BIN(nop)) { - // Write the pre call bytes - ujit_instr_entry(cb); - //add(cb, RSI, imm_opnd(8)); // increment PC //mov(cb, mem_opnd(64, RDI, 0), RSI); // write new PC to EC object, not necessary for nop bytecode? //mov(cb, RAX, RSI); // return new PC @@ -101,10 +106,34 @@ ujit_compile_insn(rb_iseq_t *iseq, size_t insn_idx) if (insn == BIN(pop)) { - // Write the pre call bytes - ujit_instr_entry(cb); + // Decrement SP + sub(cb, mem_opnd(64, RDI, 8), imm_opnd(8)); + + // Directly return the next PC, which is a constant + mov(cb, RAX, const_ptr_opnd(next_pc)); + + // Write the post call bytes + ujit_instr_exit(cb); + + addr2insn_bookkeeping(code_ptr, insn); + + return code_ptr; + } + + if (insn == BIN(putobject_INT2FIX_0_) || insn == BIN(putobject_INT2FIX_1_)) + { + // Load current SP into RAX + mov(cb, RAX, mem_opnd(64, RDI, 8)); + + // Write constant at SP + int cst = (insn == BIN(putobject_INT2FIX_0_))? 0:1; + mov(cb, mem_opnd(64, RAX, 0), imm_opnd(INT2FIX(cst))); + + // Load incremented SP into RCX + lea(cb, RCX, mem_opnd(64, RAX, 8)); - sub(cb, mem_opnd(64, RDI, 8), imm_opnd(8)); // decrement SP + // Write back incremented SP + mov(cb, mem_opnd(64, RDI, 8), RCX); // Directly return the next PC, which is a constant mov(cb, RAX, const_ptr_opnd(next_pc)); -- cgit v1.2.3