aboutsummaryrefslogtreecommitdiff
path: root/ARMeilleure/Instructions
diff options
context:
space:
mode:
authorFicture Seven <FICTURE7@gmail.com>2020-06-18 07:37:21 +0400
committerGitHub <noreply@github.com>2020-06-18 13:37:21 +1000
commit2421186d974446ef4183420c50bc37e58d9fe213 (patch)
treee182d974bc8dde8c6dcb206936cd2c4146f2f736 /ARMeilleure/Instructions
parent5e724cf24e3d696b95be859c055a617e5d37bf80 (diff)
Generalize tail continues (#1298)
* Generalize tail continues * Fix DecodeBasicBlock `Next` and `Branch` would be null, which is not the state expected by the branch instructions. They end up branching or falling into a block which is never populated by the `Translator`. This causes an assert to be fired when building the CFG. * Clean up Decode overloads * Do not synchronize when branching into exit block If we're branching into an exit block, that exit block will tail continue into another translation which already has a synchronization. * Remove A32 predicate tail continue If `block` is not an exit block then the `block.Next` must exist (as per the last instruction of `block`). * Throw if decoded 0 blocks Address gdkchan's feedback * Rebuild block list instead of setting to null Address gdkchan's feedback
Diffstat (limited to 'ARMeilleure/Instructions')
-rw-r--r--ARMeilleure/Instructions/InstEmitException.cs10
-rw-r--r--ARMeilleure/Instructions/InstEmitException32.cs5
-rw-r--r--ARMeilleure/Instructions/InstEmitFlow.cs68
-rw-r--r--ARMeilleure/Instructions/InstEmitFlow32.cs9
-rw-r--r--ARMeilleure/Instructions/InstEmitFlowHelper.cs97
5 files changed, 55 insertions, 134 deletions
diff --git a/ARMeilleure/Instructions/InstEmitException.cs b/ARMeilleure/Instructions/InstEmitException.cs
index cdf6cf34..9cfd066f 100644
--- a/ARMeilleure/Instructions/InstEmitException.cs
+++ b/ARMeilleure/Instructions/InstEmitException.cs
@@ -27,11 +27,6 @@ namespace ARMeilleure.Instructions
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id));
context.LoadFromContext();
-
- if (context.CurrBlock.Next == null)
- {
- EmitTailContinue(context, Const(op.Address + 4));
- }
}
public static void Und(ArmEmitterContext context)
@@ -45,11 +40,6 @@ namespace ARMeilleure.Instructions
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.RawOpCode));
context.LoadFromContext();
-
- if (context.CurrBlock.Next == null)
- {
- EmitTailContinue(context, Const(op.Address + 4));
- }
}
}
} \ No newline at end of file
diff --git a/ARMeilleure/Instructions/InstEmitException32.cs b/ARMeilleure/Instructions/InstEmitException32.cs
index fd64e7bf..fd67ed52 100644
--- a/ARMeilleure/Instructions/InstEmitException32.cs
+++ b/ARMeilleure/Instructions/InstEmitException32.cs
@@ -27,11 +27,6 @@ namespace ARMeilleure.Instructions
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id));
context.LoadFromContext();
-
- if (context.CurrBlock.Next == null)
- {
- EmitTailContinue(context, Const(op.Address + 4));
- }
}
}
}
diff --git a/ARMeilleure/Instructions/InstEmitFlow.cs b/ARMeilleure/Instructions/InstEmitFlow.cs
index 6f2a346e..2fcf50db 100644
--- a/ARMeilleure/Instructions/InstEmitFlow.cs
+++ b/ARMeilleure/Instructions/InstEmitFlow.cs
@@ -15,14 +15,7 @@ namespace ARMeilleure.Instructions
{
OpCodeBImmAl op = (OpCodeBImmAl)context.CurrOp;
- if (context.CurrBlock.Branch != null)
- {
- context.Branch(context.GetLabel((ulong)op.Immediate));
- }
- else
- {
- EmitTailContinue(context, Const(op.Immediate), context.CurrBlock.TailCall);
- }
+ context.Branch(context.GetLabel((ulong)op.Immediate));
}
public static void B_Cond(ArmEmitterContext context)
@@ -92,69 +85,22 @@ namespace ARMeilleure.Instructions
{
OpCodeBImm op = (OpCodeBImm)context.CurrOp;
- if (context.CurrBlock.Branch != null)
- {
- EmitCondBranch(context, context.GetLabel((ulong)op.Immediate), cond);
-
- if (context.CurrBlock.Next == null)
- {
- EmitTailContinue(context, Const(op.Address + 4));
- }
- }
- else
- {
- Operand lblTaken = Label();
-
- EmitCondBranch(context, lblTaken, cond);
-
- EmitTailContinue(context, Const(op.Address + 4));
-
- context.MarkLabel(lblTaken);
-
- EmitTailContinue(context, Const(op.Immediate));
- }
+ EmitCondBranch(context, context.GetLabel((ulong)op.Immediate), cond);
}
private static void EmitBranch(ArmEmitterContext context, Operand value, bool onNotZero)
{
OpCodeBImm op = (OpCodeBImm)context.CurrOp;
- if (context.CurrBlock.Branch != null)
+ Operand lblTarget = context.GetLabel((ulong)op.Immediate);
+
+ if (onNotZero)
{
- Operand lblTarget = context.GetLabel((ulong)op.Immediate);
-
- if (onNotZero)
- {
- context.BranchIfTrue(lblTarget, value);
- }
- else
- {
- context.BranchIfFalse(lblTarget, value);
- }
-
- if (context.CurrBlock.Next == null)
- {
- EmitTailContinue(context, Const(op.Address + 4));
- }
+ context.BranchIfTrue(lblTarget, value);
}
else
{
- Operand lblTaken = Label();
-
- if (onNotZero)
- {
- context.BranchIfTrue(lblTaken, value);
- }
- else
- {
- context.BranchIfFalse(lblTaken, value);
- }
-
- EmitTailContinue(context, Const(op.Address + 4));
-
- context.MarkLabel(lblTaken);
-
- EmitTailContinue(context, Const(op.Immediate));
+ context.BranchIfFalse(lblTarget, value);
}
}
}
diff --git a/ARMeilleure/Instructions/InstEmitFlow32.cs b/ARMeilleure/Instructions/InstEmitFlow32.cs
index 47233eb9..d7ebc945 100644
--- a/ARMeilleure/Instructions/InstEmitFlow32.cs
+++ b/ARMeilleure/Instructions/InstEmitFlow32.cs
@@ -15,14 +15,7 @@ namespace ARMeilleure.Instructions
{
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
- if (context.CurrBlock.Branch != null)
- {
- context.Branch(context.GetLabel((ulong)op.Immediate));
- }
- else
- {
- EmitTailContinue(context, Const(op.Immediate));
- }
+ context.Branch(context.GetLabel((ulong)op.Immediate));
}
public static void Bl(ArmEmitterContext context)
diff --git a/ARMeilleure/Instructions/InstEmitFlowHelper.cs b/ARMeilleure/Instructions/InstEmitFlowHelper.cs
index e99afa75..6906f782 100644
--- a/ARMeilleure/Instructions/InstEmitFlowHelper.cs
+++ b/ARMeilleure/Instructions/InstEmitFlowHelper.cs
@@ -150,17 +150,32 @@ namespace ARMeilleure.Instructions
private static void EmitNativeCall(ArmEmitterContext context, Operand nativeContextPtr, Operand funcAddr, bool isJump = false)
{
context.StoreToContext();
- Operand returnAddress;
+
if (isJump)
{
context.Tailcall(funcAddr, nativeContextPtr);
}
else
{
- returnAddress = context.Call(funcAddr, OperandType.I64, nativeContextPtr);
+ OpCode op = context.CurrOp;
+
+ Operand returnAddress = context.Call(funcAddr, OperandType.I64, nativeContextPtr);
+
context.LoadFromContext();
- EmitContinueOrReturnCheck(context, returnAddress);
+ // Note: The return value of a translated function is always an Int64 with the
+ // address execution has returned to. We expect this address to be immediately after the
+ // current instruction, if it isn't we keep returning until we reach the dispatcher.
+ Operand nextAddr = Const((long)op.Address + op.OpCodeSizeInBytes);
+
+ // Try to continue within this block.
+ // If the return address isn't to our next instruction, we need to return so the JIT can figure out what to do.
+ Operand lblContinue = context.GetLabel(nextAddr.Value);
+
+ // We need to clear out the call flag for the return address before comparing it.
+ context.BranchIfTrue(lblContinue, context.ICompareEqual(context.BitwiseAnd(returnAddress, Const(~CallFlag)), nextAddr));
+
+ context.Return(returnAddress);
}
}
@@ -191,46 +206,18 @@ namespace ARMeilleure.Instructions
}
}
- private static void EmitContinueOrReturnCheck(ArmEmitterContext context, Operand returnAddress)
- {
- // Note: The return value of a translated function is always an Int64 with the
- // address execution has returned to. We expect this address to be immediately after the
- // current instruction, if it isn't we keep returning until we reach the dispatcher.
- Operand nextAddr = Const(GetNextOpAddress(context.CurrOp));
-
- // Try to continue within this block.
- // If the return address isn't to our next instruction, we need to return so the JIT can figure out what to do.
- Operand lblContinue = Label();
-
- // We need to clear out the call flag for the return address before comparing it.
- context.BranchIfTrue(lblContinue, context.ICompareEqual(context.BitwiseAnd(returnAddress, Const(~CallFlag)), nextAddr));
-
- context.Return(returnAddress);
-
- context.MarkLabel(lblContinue);
-
- if (context.CurrBlock.Next == null)
- {
- // No code following this instruction, try and find the next block and jump to it.
- EmitTailContinue(context, nextAddr);
- }
- }
-
- private static ulong GetNextOpAddress(OpCode op)
- {
- return op.Address + (ulong)op.OpCodeSizeInBytes;
- }
-
public static void EmitTailContinue(ArmEmitterContext context, Operand address, bool allowRejit = false)
{
- bool useTailContinue = true; // Left option here as it may be useful if we need to return to managed rather than tail call in future. (eg. for debug)
+ // Left option here as it may be useful if we need to return to managed rather than tail call in future.
+ // (eg. for debug)
+ bool useTailContinue = true;
if (useTailContinue)
{
if (context.HighCq)
{
- // If we're doing a tail continue in HighCq, reserve a space in the jump table to avoid calling back to the translator.
- // This will always try to get a HighCq version of our continue target as well.
+ // If we're doing a tail continue in HighCq, reserve a space in the jump table to avoid calling back
+ // to the translator. This will always try to get a HighCq version of our continue target as well.
EmitJumpTableBranch(context, address, true);
}
else
@@ -263,6 +250,7 @@ namespace ARMeilleure.Instructions
{
address = context.BitwiseOr(address, Const(address.Type, (long)CallFlag)); // Set call flag.
Operand fallbackAddr = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), address);
+
EmitNativeCall(context, fallbackAddr, isJump);
}
@@ -273,39 +261,48 @@ namespace ARMeilleure.Instructions
Operand endLabel = Label();
Operand fallbackLabel = Label();
- Action<Operand> emitTableEntry = (Operand entrySkipLabel) =>
+ void EmitTableEntry(Operand entrySkipLabel)
{
// Try to take this entry in the table if its guest address equals 0.
Operand gotResult = context.CompareAndSwap(tableAddress, Const(0L), address);
// Is the address ours? (either taken via CompareAndSwap (0), or what was already here)
- context.BranchIfFalse(entrySkipLabel, context.BitwiseOr(context.ICompareEqual(gotResult, address), context.ICompareEqual(gotResult, Const(0L))));
+ context.BranchIfFalse(entrySkipLabel,
+ context.BitwiseOr(
+ context.ICompareEqual(gotResult, address),
+ context.ICompareEqual(gotResult, Const(0L)))
+ );
// It's ours, so what function is it pointing to?
Operand targetFunctionPtr = context.Add(tableAddress, Const(8L));
Operand targetFunction = context.Load(OperandType.I64, targetFunctionPtr);
// Call the function.
- // We pass in the entry address as the guest address, as the entry may need to be updated by the indirect call stub.
+ // We pass in the entry address as the guest address, as the entry may need to be updated by the
+ // indirect call stub.
EmitNativeCallWithGuestAddress(context, targetFunction, tableAddress, isJump);
+
context.Branch(endLabel);
- };
+ }
// Currently this uses a size of 1, as higher values inflate code size for no real benefit.
for (int i = 0; i < JumpTable.DynamicTableElems; i++)
{
if (i == JumpTable.DynamicTableElems - 1)
{
- emitTableEntry(fallbackLabel); // If this is the last entry, avoid emitting the additional label and add.
- }
+ // If this is the last entry, avoid emitting the additional label and add.
+ EmitTableEntry(fallbackLabel);
+ }
else
{
Operand nextLabel = Label();
- emitTableEntry(nextLabel);
+ EmitTableEntry(nextLabel);
context.MarkLabel(nextLabel);
- tableAddress = context.Add(tableAddress, Const((long)JumpTable.JumpTableStride)); // Move to the next table entry.
+
+ // Move to the next table entry.
+ tableAddress = context.Add(tableAddress, Const((long)JumpTable.JumpTableStride));
}
}
@@ -323,16 +320,15 @@ namespace ARMeilleure.Instructions
address = context.ZeroExtend32(OperandType.I64, address);
}
- // TODO: Constant folding. Indirect calls are slower in the best case and emit more code so we want to avoid them when possible.
+ // TODO: Constant folding. Indirect calls are slower in the best case and emit more code so we want to
+ // avoid them when possible.
bool isConst = address.Kind == OperandKind.Constant;
long constAddr = (long)address.Value;
if (!context.HighCq)
{
- // Don't emit indirect calls or jumps if we're compiling in lowCq mode.
- // This avoids wasting space on the jump and indirect tables.
- // Just ask the translator for the function address.
-
+ // Don't emit indirect calls or jumps if we're compiling in lowCq mode. This avoids wasting space on the
+ // jump and indirect tables. Just ask the translator for the function address.
EmitBranchFallback(context, address, isJump);
}
else if (!isConst)
@@ -376,7 +372,8 @@ namespace ARMeilleure.Instructions
Operand funcAddr = context.Load(OperandType.I64, tableEntryPtr);
- EmitNativeCallWithGuestAddress(context, funcAddr, address, isJump); // Call the function directly. If it's not present yet, this will call the direct call stub.
+ // Call the function directly. If it's not present yet, this will call the direct call stub.
+ EmitNativeCallWithGuestAddress(context, funcAddr, address, isJump);
}
}
}