diff options
Diffstat (limited to 'chapter21/src')
| -rw-r--r-- | chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java | 17 | ||||
| -rw-r--r-- | chapter21/src/test/java/com/seaofnodes/simple/codegen/X86EncodingTest.java | 65 |
2 files changed, 72 insertions, 10 deletions
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java index b23249b8..2d611931 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NotX86.java @@ -17,23 +17,20 @@ public class NotX86 extends MachConcreteNode implements MachNode { short dst = enc.reg(this ); short src = enc.reg(in(1)); - // Pre-zero using XOR dst,dst; since zero'd will not have a - // byte-dependency from the setz. Can skip REX is dst is low 8, makes - // this a 32b xor, which will also zero the high bits. - if( dst >= 8 ) enc.add1(x86_64_v2.rex(dst, dst, 0)); - enc.add1(0x33); // opcode - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst)); - // test rdi,rdi enc.add1(x86_64_v2.rex(src, src, 0)); enc.add1(0x85); enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, src, src)); - // setz (sete dil) - enc.add1(x86_64_v2.rex(dst, 0, 0)); + // setz dstb; then zero-extend the low byte into the full register. + if( dst >= 4 ) enc.add1(x86_64_v2.rex(0, dst, 0, false)); enc.add1(0x0F); enc.add1(0x94); - enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, 0)); + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, dst)); + if( dst >= 4 ) enc.add1(x86_64_v2.rex(dst, dst, 0, false)); + enc.add1(0x0F); + enc.add1(0xB6); + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, dst)); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); } } diff --git a/chapter21/src/test/java/com/seaofnodes/simple/codegen/X86EncodingTest.java b/chapter21/src/test/java/com/seaofnodes/simple/codegen/X86EncodingTest.java index 03a57675..368c243b 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/codegen/X86EncodingTest.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/codegen/X86EncodingTest.java @@ -1,10 +1,55 @@ package com.seaofnodes.simple.codegen; +import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.node.cpus.x86_64_v2.CmpMemX86; +import com.seaofnodes.simple.node.cpus.x86_64_v2.NotX86; import org.junit.Test; import static org.junit.Assert.*; public class X86EncodingTest { + + @Test + public void testLogicalNotRegisters() { + CodeGen code = new CodeGen("return !arg;") + .driver(CodeGen.Phase.InstSelect, "x86_64_v2", "SystemV"); + var not = code._stop.walk(n -> n instanceof NotX86 ? (NotX86)n : null); + assertNotNull("Must exercise logical not", not); + int[][] registers = {{0,0}, {1,1}, {7,7}, {9,9}, {1,9}, {9,1}, {6,0}, {15,8}}; + // TEST src,src; SETZ dstb; MOVZX dst32,dstb. Include overlapping + // registers, low-byte registers requiring REX, and extended registers. + String[] expected = { + "4885c00f94c00fb6c0", "4885c90f94c10fb6c9", + "4885ff400f94c7400fb6ff", "4d85c9410f94c1450fb6c9", + "4d85c90f94c10fb6c9", "4885c9410f94c1450fb6c9", + "4885c0400f94c6400fb6f6", "4d85c0410f94c7450fb6ff" + }; + for (int i = 0; i < registers.length; i++) { + int dst = registers[i][0], src = registers[i][1]; + var enc = new FixedRegisterEncoding(code, not, dst, not.in(1), src); + not.encoding(enc); + assertArrayEquals("Logical not dst=" + dst + ", src=" + src, + java.util.HexFormat.of().parseHex(expected[i]), enc._bits.toByteArray()); + } + } + + @Test + public void testLogicalNotExecution() throws java.io.IOException { + var dir = java.nio.file.Path.of("build/objs"); + java.nio.file.Files.createDirectories(dir); + java.nio.file.Files.writeString(dir.resolve("x86LogicalNot.smp"), + "val logicalNot = { int x -> return !x; };"); + java.nio.file.Files.writeString(dir.resolve("x86LogicalNot.c"), """ + #include <limits.h> + extern long long logicalNot(long long); + int main(void) { + long long inputs[] = {0, 1, -1, LLONG_MIN, LLONG_MAX, 1LL << 32}; + for (unsigned i = 0; i < sizeof(inputs)/sizeof(inputs[0]); i++) + if (logicalNot(inputs[i]) != (inputs[i] == 0)) return 1; + return 0; + } + """); + com.seaofnodes.simple.TestC.run("build/objs", "x86LogicalNot", "", -1); + } @Test public void testMemoryCompare64Immediate32() { for (int immediate : new int[]{123456789, -123456789}) { @@ -29,4 +74,24 @@ public class X86EncodingTest { assertEquals(0x94, enc.read1(start + 10)); } } + + /** Exercise instruction encoders with explicit registers, independent of allocator choices. */ + private static class FixedRegisterEncoding extends Encoding { + private final Node _dst, _src; + private final short _dstReg, _srcReg; + + FixedRegisterEncoding(CodeGen code, Node dst, int dstReg, Node src, int srcReg) { + super(code); + _dst = dst; + _src = src; + _dstReg = (short)dstReg; + _srcReg = (short)srcReg; + } + + @Override public short reg(Node n) { + if (n == _dst) return _dstReg; + if (n == _src) return _srcReg; + throw new AssertionError("Unexpected register lookup"); + } + } } |
