Skip to content

Commit

Permalink
Add MOVD on SSE2
Browse files Browse the repository at this point in the history
  • Loading branch information
YaSuenag committed Dec 28, 2024
1 parent 970581c commit 3b25450
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 0 deletions.
41 changes: 41 additions & 0 deletions src/main/java/com/yasuenag/ffmasm/amd64/SSEAsmBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,45 @@ public SSEAsmBuilder movdquMR(Register r, Register m, OptionalInt disp){
return movdq(r, m, disp, (byte)0xf3, (byte)0x7f);
}

private SSEAsmBuilder movDorQ(Register r, Register m, OptionalInt disp, byte secondOpcode){
byteBuf.put((byte)0x66); // prefix
emitREXOp(r, m);
byteBuf.put((byte)0x0f); // escape opcode
byteBuf.put(secondOpcode);
var mode = emitModRM(r, m, disp);
emitDisp(mode, disp, m);

return this;
}

/**
* Move doubleword from r/m32 to xmm.
* Opcode: 66 0F 6E /r
* Instruction: MOVD xmm, r/m32
* Op/En: A
*
* @param r "r" register
* @param m "r/m" register
* @param disp Displacement. Set "empty" if this operation is reg-reg.
* @return This instance
*/
public SSEAsmBuilder movdRM(Register r, Register m, OptionalInt disp){
return movDorQ(r, m, disp, (byte)0x6e);
}

/**
* Move doubleword from xmm register to r/m32.
* Opcode: 66 0F 7E /r
* Instruction: MOVD r/m32, xmm
* Op/En: B
*
* @param r "r" register
* @param m "r/m" register
* @param disp Displacement. Set "empty" if this operation is reg-reg.
* @return This instance
*/
public SSEAsmBuilder movdMR(Register r, Register m, OptionalInt disp){
return movDorQ(r, m, disp, (byte)0x7e);
}

}
71 changes: 71 additions & 0 deletions src/test/java/com/yasuenag/ffmasm/test/amd64/SSEAsmTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,75 @@ public void testMOVDQU(){
}
}

/**
* Tests MOVD A
*/
@Test
@EnabledOnOs({OS.LINUX, OS.WINDOWS})
public void testMOVD_A(){
try(var seg = new CodeSegment()){
var desc = FunctionDescriptor.of(
ValueLayout.JAVA_FLOAT, // return value
ValueLayout.ADDRESS // 1st argument
);
var method = AMD64AsmBuilder.create(SSEAsmBuilder.class, seg, desc)
/* push %rbp */ .push(Register.RBP)
/* mov %rsp, %rbp */ .movMR(Register.RSP, Register.RBP, OptionalInt.empty())
.cast(SSEAsmBuilder.class)
/* movd (arg1), %xmm0 */ .movdRM(Register.XMM0, argReg.arg1(), OptionalInt.of(0))
/* leave */ .leave()
/* ret */ .ret()
.build();

float expected = 1.1f;
var arena = Arena.ofAuto();
MemorySegment src = arena.allocate(ValueLayout.JAVA_FLOAT);
src.set(ValueLayout.JAVA_FLOAT, 0, expected);

float actual = (float)method.invoke(src);

Assertions.assertEquals(expected, actual);
}
catch(Throwable t){
Assertions.fail(t);
}
}

/**
* Tests MOVD B
*/
@Test
@EnabledOnOs({OS.LINUX, OS.WINDOWS})
public void testMOVD_B(){
try(var seg = new CodeSegment()){
var arena = Arena.ofAuto();
MemorySegment dest = arena.allocate(ValueLayout.JAVA_FLOAT);

var desc = FunctionDescriptor.ofVoid(
ValueLayout.JAVA_FLOAT // 1st argument
);
var method = AMD64AsmBuilder.create(SSEAsmBuilder.class, seg, desc)
/* push %rbp */ .push(Register.RBP)
/* mov %rsp, %rbp */ .movMR(Register.RSP, Register.RBP, OptionalInt.empty())
// Mixed argument order (int, fp) is different between Windows and Linux.
// Thus address is loaded from immediate value.
/* mov addr, %rax */ .movImm(Register.RAX, dest.address())
.cast(SSEAsmBuilder.class)
/* movd %xmm0, (%rax) */ .movdMR(Register.XMM0, Register.RAX, OptionalInt.of(0))
/* leave */ .leave()
/* ret */ .ret()
.build();

float expected = 1.1f;

method.invoke(expected);
float actual = dest.get(ValueLayout.JAVA_FLOAT, 0);

Assertions.assertEquals(expected, actual);
}
catch(Throwable t){
Assertions.fail(t);
}
}

}

0 comments on commit 3b25450

Please sign in to comment.