Skip to content

Commit

Permalink
Emit the jump tables in a slow path
Browse files Browse the repository at this point in the history
  • Loading branch information
Quan Anh Mai committed Dec 9, 2024
1 parent 137fd0b commit 565adf0
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4544,6 +4544,26 @@ public final void testl(Register dst, int imm32) {
}
}

/**
* Emit a UD2 instruction, this signals the processor to stop decoding instructions further in
* the fallthrough path (Intel Optimization Reference Manual Volume 1, section 3.4.1.5, Branch
* Type Selection, Assembly/Compiler coding rule 13).
* <p>
* This also helps when we want to emit data in the code section as it prevents mismatched
* instructions when decoding from different paths. E.g. consider this piece of hex code:
* <p>
* {@code 01 48 01 c8}
* <p>
* With {@code 01} being the data and {@code 48 01 c8} being {@code add rax, rcx}. However, if
* the decoder starts with {@code 01} it will see the next instruction being {@code 01 48 01}
* which is {@code add [rax + 1], ecx}. This mismatch invalidates the uop cache as the CPU
* cannot know which instruction sequence is the correct one.
*/
public void ud2() {
emitByte(0x0F);
emitByte(0x0B);
}

public final void vzeroupper() {
emitVEX(VEXPrefixConfig.L128, VEXPrefixConfig.P_, VEXPrefixConfig.M_0F, VEXPrefixConfig.W0, 0, 0);
emitByte(0x77);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -457,11 +457,15 @@ public static void emitJumpTable(CompilationResultBuilder crb, AArch64MacroAssem
// jump to target
masm.jmp(scratch);

masm.bind(jumpTable);
// emit jump table entries
targets.forEach(label -> masm.emitJumpTableOffset(jumpTable, label));
JumpTable jt = new JumpTable(jumpTable.position(), lowKey, highKey, EntryFormat.OFFSET_ONLY);
crb.compilationResult.addAnnotation(jt);
crb.getLIR().addSlowPath(null, () -> {
// Insert halt so that static analyzers do not continue decoding past this point
masm.halt();
masm.bind(jumpTable);
// emit jump table entries
targets.forEach(label -> masm.emitJumpTableOffset(jumpTable, label));
JumpTable jt = new JumpTable(jumpTable.position(), lowKey, highKey, EntryFormat.OFFSET_ONLY);
crb.compilationResult.addAnnotation(jt);
});
}
}

Expand Down Expand Up @@ -542,18 +546,22 @@ public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) {
masm.jmp(jumpTableBase);
}

// ensure jump table is aligned with the entry size
masm.align(format.size);
masm.bind(jumpTable);
// emit jump table entries
for (int i = 0; i < targets.length; i++) {
if (format == EntryFormat.VALUE_AND_OFFSET) {
masm.emitInt(keys[i].asInt());
crb.getLIR().addSlowPath(this, () -> {
// Insert halt so that static analyzers do not continue decoding past this point
masm.halt();
// ensure jump table is aligned with the entry size
masm.align(format.size);
masm.bind(jumpTable);
// emit jump table entries
for (int i = 0; i < targets.length; i++) {
if (format == EntryFormat.VALUE_AND_OFFSET) {
masm.emitInt(keys[i].asInt());
}
masm.emitJumpTableOffset(jumpTable, targets[i].label());
}
masm.emitJumpTableOffset(jumpTable, targets[i].label());
}
JumpTable jt = new JumpTable(jumpTable.position(), 0, keys.length - 1, format);
crb.compilationResult.addAnnotation(jt);
JumpTable jt = new JumpTable(jumpTable.position(), 0, keys.length - 1, format);
crb.compilationResult.addAnnotation(jt);
});
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -711,32 +711,37 @@ public static void emitJumpTable(CompilationResultBuilder crb, AMD64MacroAssembl
masm.addq(scratchReg, idxScratchReg);
masm.jmp(scratchReg);

// Inserting padding so that jump table address is 4-byte aligned
masm.align(4);

// Patch LEA instruction above now that we know the position of the jump table
// this is ugly but there is no better way to do this given the assembler API
final int jumpTablePos = masm.position();
final int leaDisplacementPosition = afterLea - 4;
masm.emitInt(jumpTablePos - afterLea, leaDisplacementPosition);

// Emit jump table entries
targets.forEach(label -> {
int offsetToJumpTableBase = masm.position() - jumpTablePos;
if (label.isBound()) {
int imm32 = label.position() - jumpTablePos;
masm.emitInt(imm32);
} else {
label.addPatchAt(masm.position(), masm);

masm.emitByte(0); // pseudo-opcode for jump table entry
masm.emitShort(offsetToJumpTableBase);
masm.emitByte(0); // padding to make jump table entry 4 bytes wide
}
crb.getLIR().addSlowPath(null, () -> {
// Insert halt so that static analyzers do not continue decoding past this point
masm.hlt();
// Insert ud2 so the CPU does not continue decoding past this point
masm.ud2();
// Inserting padding so that jump table address is 4-byte aligned
masm.align(4);
// Patch LEA instruction above now that we know the position of the jump table
// this is ugly but there is no better way to do this given the assembler API
int jumpTablePos = masm.position();
int leaDisplacementPosition = afterLea - 4;
masm.emitInt(jumpTablePos - afterLea, leaDisplacementPosition);

// Emit jump table entries
targets.forEach(label -> {
int offsetToJumpTableBase = masm.position() - jumpTablePos;
if (label.isBound()) {
int imm32 = label.position() - jumpTablePos;
masm.emitInt(imm32);
} else {
label.addPatchAt(masm.position(), masm);

masm.emitByte(0); // pseudo-opcode for jump table entry
masm.emitShort(offsetToJumpTableBase);
masm.emitByte(0); // padding to make jump table entry 4 bytes wide
}
});

JumpTable jt = new JumpTable(jumpTablePos, lowKey, highKey, EntryFormat.OFFSET_ONLY);
crb.compilationResult.addAnnotation(jt);
});

JumpTable jt = new JumpTable(jumpTablePos, lowKey, highKey, EntryFormat.OFFSET_ONLY);
crb.compilationResult.addAnnotation(jt);
}
}

Expand Down Expand Up @@ -794,38 +799,43 @@ public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
masm.addq(scratchReg, entryScratchReg);
masm.jmp(scratchReg);

// Inserting padding so that jump the table address is aligned
EntryFormat entryFormat = defaultTarget == null ? EntryFormat.OFFSET_ONLY : EntryFormat.VALUE_AND_OFFSET;
masm.align(entryFormat.size);

// Patch LEA instruction above now that we know the position of the jump table
// this is ugly but there is no better way to do this given the assembler API
final int jumpTablePos = masm.position();
final int leaDisplacementPosition = afterLea - 4;
masm.emitInt(jumpTablePos - afterLea, leaDisplacementPosition);

// Emit jump table entries
for (int i = 0; i < targets.length; i++) {

Label label = targets[i].label();

if (defaultTarget != null) {
masm.emitInt(keys[i].asInt());
crb.getLIR().addSlowPath(this, () -> {
// Insert halt so that static analyzers do not continue decoding past this point
masm.hlt();
// Insert ud2 so the CPU does not continue decoding past this point
masm.ud2();
// Inserting padding so that jump the table address is aligned
EntryFormat entryFormat = defaultTarget == null ? EntryFormat.OFFSET_ONLY : EntryFormat.VALUE_AND_OFFSET;
masm.align(entryFormat.size);

// Patch LEA instruction above now that we know the position of the jump table
// this is ugly but there is no better way to do this given the assembler API
final int jumpTablePos = masm.position();
final int leaDisplacementPosition = afterLea - 4;
masm.emitInt(jumpTablePos - afterLea, leaDisplacementPosition);

// Emit jump table entries
for (int i = 0; i < targets.length; i++) {
Label label = targets[i].label();

if (defaultTarget != null) {
masm.emitInt(keys[i].asInt());
}
if (label.isBound()) {
int imm32 = label.position() - jumpTablePos;
masm.emitInt(imm32);
} else {
int offsetToJumpTableBase = masm.position() - jumpTablePos;
label.addPatchAt(masm.position(), masm);
masm.emitByte(0); // pseudo-opcode for jump table entry
masm.emitShort(offsetToJumpTableBase);
masm.emitByte(0); // padding to make jump table entry 4 bytes wide
}
}
if (label.isBound()) {
int imm32 = label.position() - jumpTablePos;
masm.emitInt(imm32);
} else {
int offsetToJumpTableBase = masm.position() - jumpTablePos;
label.addPatchAt(masm.position(), masm);
masm.emitByte(0); // pseudo-opcode for jump table entry
masm.emitShort(offsetToJumpTableBase);
masm.emitByte(0); // padding to make jump table entry 4 bytes wide
}
}

JumpTable jt = new JumpTable(jumpTablePos, 0, keys.length - 1, entryFormat);
crb.compilationResult.addAnnotation(jt);
JumpTable jt = new JumpTable(jumpTablePos, 0, keys.length - 1, entryFormat);
crb.compilationResult.addAnnotation(jt);
});
}
}

Expand Down

0 comments on commit 565adf0

Please sign in to comment.