From 787a37d6b1753bb7c6abd36cba293737ad6b9a0a Mon Sep 17 00:00:00 2001 From: Dibyendu Majumdar Date: Sat, 19 Aug 2023 08:52:57 -0700 Subject: Chapter 1: Introduction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cliff Click Co-authored-by: Thalia Archibald Co-authored-by: hadha Co-authored-by: XmiliaH <45106915+XmiliaH@users.noreply.github.com> Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> --- isa/src/test/java/com/seaofnodes/isa/RiscTest.java | 59 ++++++++++ isa/src/test/java/com/seaofnodes/isa/X86Test.java | 124 +++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 isa/src/test/java/com/seaofnodes/isa/RiscTest.java create mode 100644 isa/src/test/java/com/seaofnodes/isa/X86Test.java (limited to 'isa/src/test/java/com') diff --git a/isa/src/test/java/com/seaofnodes/isa/RiscTest.java b/isa/src/test/java/com/seaofnodes/isa/RiscTest.java new file mode 100644 index 00000000..e26eb20b --- /dev/null +++ b/isa/src/test/java/com/seaofnodes/isa/RiscTest.java @@ -0,0 +1,59 @@ +package com.seaofnodes.isa; + +import com.seaofnodes.isa.eval.EvalArm64; +import com.seaofnodes.isa.eval.EvalRisc5; + +/** Fixed instruction words plus execution checks of the shared test emulators. */ +public class RiscTest { + static void word(int expected, int actual) { + assert expected==actual : String.format("Expected %08x, got %08x",expected,actual); + } + public static void main(String[] args) { + word(0x8B020020,Arm64.r_reg(Arm64.OP_ADD,0,2,0,1,0)); // add x0,x1,x2 + word(0x9100A420,Arm64.imm_inst(Arm64.OPI_ADD,41,1,0)); + word(0xD2800540,Arm64.mov(Arm64.OP_MOVZ,0,42,0)); + word(0xF9400BE0,Arm64.load_str_imm(Arm64.OP_LOAD_IMM_64,16,31,0,8)); + word(0xF9000BE0,Arm64.load_str_imm(Arm64.OP_STORE_IMM_64,16,31,0,8)); + word(0xD65F03C0,Arm64.ret(Arm64.OP_RET)); + word(0x17FFFFFF,Arm64.b(Arm64.OP_UJMP,-4)); + word(0x16000000,Arm64.b(Arm64.OP_UJMP,-(1<<27))); + word(0x54000040,Arm64.b_cond(Arm64.OP_BRANCH,8,Arm64.COND.EQ)); + word(0x54800001,Arm64.b_cond(Arm64.OP_BRANCH,-(1<<20),Arm64.COND.NE)); + word(0x5CFFFFE0,Arm64.load_pc(Arm64.OPF_ARM,-4,0)); + + word(0x002081B3,RiscV.r_type(RiscV.OP,3,0,1,2,0)); // add x3,x1,x2 + word(0x02A00513,RiscV.i_type(RiscV.OP_IMM,10,0,0,42)); + word(0x123451B7,RiscV.u_type(RiscV.OP_LUI,3,0x12345)); + word(0x00B13823,RiscV.s_type(RiscV.OP_STORE,3,2,11,16)); + word(0x00208463,RiscV.b_type(RiscV.OP_BRANCH,0,(short)1,(short)2,8)); + word(0xFE208EE3,RiscV.b_type(RiscV.OP_BRANCH,0,(short)1,(short)2,-4)); + word(0x008000EF,RiscV.j_type(RiscV.OP_JAL,1,8)); + word(0xFFDFF0EF,RiscV.j_type(RiscV.OP_JAL,1,-4)); + word(0x000800EF,RiscV.j_type(RiscV.OP_JAL,1,1<<19)); + word(0x800000EF,RiscV.j_type(RiscV.OP_JAL,1,-(1<<20))); + + // Constants exercise MOVN, MOVZ and multiple MOVK lanes, including all 0/1. + for( long x : new long[]{0,-1,42,0xFFFF0000FFFFL,0x1122334455667788L,Long.MIN_VALUE} ) { + var bytes=new X86Test.Bytes(); + Arm64.constant(bytes,0,x); + byte[] image=new byte[1024]; + System.arraycopy(bytes.toByteArray(),0,image,4,bytes.size()); + var arm=new EvalArm64(image,512); + arm._pc=4; + arm.step(bytes.size()/4); + assert arm.regs[0]==x : String.format("Constant %016x became %016x",x,arm.regs[0]); + } + // Raw instructions make these emulator checks independent of the encoder. + var arm=new EvalArm64(new byte[1024],512); + arm.st4(4,0xD2800540); // mov x0,#42 + arm._pc=4; + arm.step(1); + assert arm.regs[0]==42; + var risc=new EvalRisc5(new byte[1024],512); + risc.st4(4,0x02A00513); // addi a0,zero,42 + risc._pc=4; + risc.step(1); + assert risc.regs[10]==42; + System.out.println("Shared ARM/RISC-V encoding and evaluator tests passed"); + } +} diff --git a/isa/src/test/java/com/seaofnodes/isa/X86Test.java b/isa/src/test/java/com/seaofnodes/isa/X86Test.java new file mode 100644 index 00000000..e3aa2699 --- /dev/null +++ b/isa/src/test/java/com/seaofnodes/isa/X86Test.java @@ -0,0 +1,124 @@ +package com.seaofnodes.isa; + +import java.io.ByteArrayOutputStream; +import java.util.HexFormat; +import java.util.function.Consumer; + +/** Fixed instruction bytes, independent of the compiler and register allocator. */ +public class X86Test { + static class Bytes extends ByteArrayOutputStream implements CodeSink { + @Override public Bytes add1(int value) { write(value); return this; } + } + static void check(String expected, Consumer emit) { + var bytes=new Bytes(); + emit.accept(bytes); + String actual=HexFormat.of().formatHex(bytes.toByteArray()); + assert expected.equals(actual) : "Expected "+expected+", got "+actual; + } + public static void main(String[] args) { + check("4d03ca", b -> X86.reg(b,0x03,9,10)); + check("4d0fafca", b -> X86.reg(b,0x0FAF,9,10)); + check("4981c17fffffff", b -> X86.imm(b,0x81,0,9,-129)); + check("4983c180", b -> X86.imm(b,0x81,0,9,-128)); + check("4983c17f", b -> X86.imm(b,0x81,0,9,127)); + check("4981c180000000", b -> X86.imm(b,0x81,0,9,128)); + check("4d6bca80", b -> X86.imul(b,9,10,-128)); + check("4d69ca80000000", b -> X86.imul(b,9,10,128)); + + // TEST; SETZ; MOVZX. Both overlapping and distinct input/output registers. + int[][] regs={{0,0},{1,1},{7,7},{9,9},{1,9},{9,1},{6,0},{15,8}}; + String[] not={"4885c00f94c00fb6c0","4885c90f94c10fb6c9", + "4885ff400f94c7400fb6ff","4d85c9410f94c1450fb6c9", + "4d85c90f94c10fb6c9","4885c9410f94c1450fb6c9", + "4885c0400f94c6400fb6f6","4d85c0410f94c7450fb6ff"}; + for( int i=0; i X86.not(b,dst,src)); + } + + // SIB, rbp/r13 zero displacement, displacement boundaries, and an extended index. + check("488b0424", b -> X86.load(b,3,false,false,0,4,-1,0,0)); + check("498b0424", b -> X86.load(b,3,false,false,0,12,-1,0,0)); + check("488b4500", b -> X86.load(b,3,false,false,0,5,-1,0,0)); + check("498b4500", b -> X86.load(b,3,false,false,0,13,-1,0,0)); + check("488b437f", b -> X86.load(b,3,false,false,0,3,-1,127,0)); + check("488b8380000000", b -> X86.load(b,3,false,false,0,3,-1,128,0)); + check("488b4380", b -> X86.load(b,3,false,false,0,3,-1,-128,0)); + check("488b837fffffff", b -> X86.load(b,3,false,false,0,3,-1,-129,0)); + check("4b8b44e508", b -> X86.load(b,3,false,false,0,13,12,8,3)); + check("4f8d4ca508", b -> X86.lea(b,9,13,12,8,2)); + check("4e8d0ce520000000", b -> X86.lea(b,9,-1,12,32,3)); + + check("480fbe03", b -> X86.load(b,0,true,false,0,3,-1,0,0)); + check("480fb603", b -> X86.load(b,0,false,false,0,3,-1,0,0)); + check("480fbf03", b -> X86.load(b,1,true,false,0,3,-1,0,0)); + check("480fb703", b -> X86.load(b,1,false,false,0,3,-1,0,0)); + check("486303", b -> X86.load(b,2,true,false,0,3,-1,0,0)); + check("8b03", b -> X86.load(b,2,false,false,0,3,-1,0,0)); + check("f3470f104ca508", b -> X86.load(b,2,false,true,9,13,12,8,2)); + check("f2470f104ca508", b -> X86.load(b,3,false,true,9,13,12,8,2)); + + check("408823", b -> X86.store(b,0,false,4,3,-1,0,0)); // spl, not ah + check("408833", b -> X86.store(b,0,false,6,3,-1,0,0)); // sil, not dh + check("44883b", b -> X86.store(b,0,false,15,3,-1,0,0)); + check("6647894ca508", b -> X86.store(b,1,false,9,13,12,8,2)); + check("47894ca508", b -> X86.store(b,2,false,9,13,12,8,2)); + check("4f894ca508", b -> X86.store(b,3,false,9,13,12,8,2)); + check("f3470f114ca508", b -> X86.store(b,2,true,9,13,12,8,2)); + check("f2470f114ca508", b -> X86.store(b,3,true,9,13,12,8,2)); + check("c603ff", b -> X86.storeImm(b,0,-1,3,-1,0,0)); + check("6643c744a5083412", b -> X86.storeImm(b,1,0x1234,13,12,8,2)); + check("c70378563412", b -> X86.storeImm(b,2,0x12345678,3,-1,0,0)); + check("48c703ffffffff", b -> X86.storeImm(b,3,-1,3,-1,0,0)); + check("803bff", b -> X86.cmpImm(b,0,-1,3,-1,0,0)); + check("6643817ca5083412", b -> X86.cmpImm(b,1,0x1234,13,12,8,2)); + check("813b78563412", b -> X86.cmpImm(b,2,0x12345678,3,-1,0,0)); + check("48813b15cd5b07", b -> X86.cmpImm(b,3,123456789,3,-1,0,0)); + check("49f7da", b -> X86.unary(b,0xF7,3,10)); + check("49c1e1ff", b -> X86.shift(b,4,9,255)); + check("489949f7fa", b -> X86.divide(b,10)); + check("f2450f58ca", b -> X86.sse(b,0xF2,0x0F58,false,9,10)); + check("f24d0f2aca", b -> X86.sse(b,0xF2,0x0F2A,true,9,10)); + check("66450f2eca", b -> X86.sse(b,0x66,0x0F2E,false,9,10)); + check("4d0fbeca", b -> X86.extend(b,8,true,9,10)); + check("4c0fb6ce", b -> X86.extend(b,8,false,9,6)); + check("6647394ca508", b -> X86.cmpMem(b,1,9,13,12,8,2,false)); + check("66473b4ca508", b -> X86.cmpMem(b,1,9,13,12,8,2,true)); + check("403833", b -> X86.cmpMem(b,0,6,3,-1,0,0,false)); + check("400f95c6400fb6f6", b -> X86.set(b,5,6)); + check("410f95c1450fb6c9", b -> X86.set(b,5,9)); + check("4d33c9", b -> X86.constant(b,9,0)); + check("49c7c1ffffffff", b -> X86.constant(b,9,-1)); + check("41b9ffffffff", b -> X86.constant(b,9,0xFFFFFFFFL)); + check("49b98877665544332211", b -> X86.constant(b,9,0x1122334455667788L)); + check("4c8d0d00000000", b -> { assert X86.rip(b,0,0x8D,true,9)==7; }); + check("f2440f100d00000000", b -> { assert X86.rip(b,0xF2,0x0F10,false,9)==9; }); + check("e800000000c3", b -> { X86.call(b); X86.ret(b); }); + check("ffd0", b -> X86.callRegister(b,0)); + check("41ffd2", b -> X86.callRegister(b,10)); + check("41519d", b -> X86.flags(b,9,true)); + check("9c4159", b -> X86.flags(b,9,false)); + check("ff7424088f442410", b -> X86.stackCopy(b,8,16)); + check("4d8bca", b -> X86.move(b,false,false,9,10)); + check("4d0f28ca", b -> X86.move(b,true,true,9,10)); + check("664d0f6eca", b -> X86.move(b,true,false,9,10)); + check("664d0f7ed1", b -> X86.move(b,false,true,9,10)); + branch("eb7f",129,-1); + branch("eb80",-126,-1); + branch("e97d000000",130,-1); + branch("e97cffffff",-127,-1); + branch("757f",129,5); + branch("0f857c000000",130,5); + branch("0f857bffffff",-127,5); + System.out.println("Shared x86 encoding tests passed"); + } + static void branch(String expected, int delta, int condition) { + int size=X86.branchSize(delta,condition>=0); + byte[] bytes=new byte[size]; + var shortForm=new Bytes(); + X86.branch(shortForm,condition); + System.arraycopy(shortForm.toByteArray(),0,bytes,0,2); + X86.patchBranch(bytes,0,size,delta,condition); + assert expected.equals(HexFormat.of().formatHex(bytes)); + } +} -- cgit v1.2.3