Implement arm_SVC
This commit is contained in:
parent
672ffb93d0
commit
9b2aff166a
8 changed files with 93 additions and 5 deletions
|
@ -250,6 +250,15 @@ void EmitX64::EmitBXWritePC(IR::Value* value_) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitCallSupervisor(IR::Value* value_) {
|
||||||
|
auto value = reinterpret_cast<IR::Inst*>(value_);
|
||||||
|
|
||||||
|
auto imm32 = value->GetArg(0).get();
|
||||||
|
reg_alloc.HostCall(nullptr, imm32);
|
||||||
|
|
||||||
|
code->ABI_CallFunction(reinterpret_cast<void*>(cb.CallSVC));
|
||||||
|
}
|
||||||
|
|
||||||
void EmitX64::EmitGetCarryFromOp(IR::Value*) {
|
void EmitX64::EmitGetCarryFromOp(IR::Value*) {
|
||||||
ASSERT_MSG(0, "should never happen");
|
ASSERT_MSG(0, "should never happen");
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ private:
|
||||||
void EmitGetVFlag(IR::Value* value);
|
void EmitGetVFlag(IR::Value* value);
|
||||||
void EmitSetVFlag(IR::Value* value);
|
void EmitSetVFlag(IR::Value* value);
|
||||||
void EmitBXWritePC(IR::Value* value);
|
void EmitBXWritePC(IR::Value* value);
|
||||||
|
void EmitCallSupervisor(IR::Value* value);
|
||||||
void EmitGetCarryFromOp(IR::Value* value);
|
void EmitGetCarryFromOp(IR::Value* value);
|
||||||
void EmitGetOverflowFromOp(IR::Value* value);
|
void EmitGetOverflowFromOp(IR::Value* value);
|
||||||
void EmitLeastSignificantHalf(IR::Value* value);
|
void EmitLeastSignificantHalf(IR::Value* value);
|
||||||
|
|
|
@ -59,7 +59,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename V>
|
template <typename V>
|
||||||
static const std::array<ArmMatcher<V>, 1> g_arm_instruction_table = {
|
static const std::array<ArmMatcher<V>, 2> g_arm_instruction_table = {
|
||||||
|
|
||||||
#define INST(fn, name, bitstring) detail::detail<ArmMatcher, u32, 32>::GetMatcher<decltype(fn), fn>(name, bitstring)
|
#define INST(fn, name, bitstring) detail::detail<ArmMatcher, u32, 32>::GetMatcher<decltype(fn), fn>(name, bitstring)
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ static const std::array<ArmMatcher<V>, 1> g_arm_instruction_table = {
|
||||||
|
|
||||||
// Exception Generating instructions
|
// Exception Generating instructions
|
||||||
//INST(&V::arm_BKPT, "BKPT", "cccc00010010vvvvvvvvvvvv0111vvvv"), // v5
|
//INST(&V::arm_BKPT, "BKPT", "cccc00010010vvvvvvvvvvvv0111vvvv"), // v5
|
||||||
//INST(&V::arm_SVC, "SVC", "cccc1111vvvvvvvvvvvvvvvvvvvvvvvv"), // all
|
INST(&V::arm_SVC, "SVC", "cccc1111vvvvvvvvvvvvvvvvvvvvvvvv"), // all
|
||||||
INST(&V::arm_UDF, "UDF", "111001111111------------1111----"), // all
|
INST(&V::arm_UDF, "UDF", "111001111111------------1111----"), // all
|
||||||
|
|
||||||
// Extension instructions
|
// Extension instructions
|
||||||
|
|
|
@ -326,8 +326,12 @@ public:
|
||||||
|
|
||||||
// Exception generation instructions
|
// Exception generation instructions
|
||||||
std::string arm_BKPT(Cond cond, Imm12 imm12, Imm4 imm4) { return "ice"; }
|
std::string arm_BKPT(Cond cond, Imm12 imm12, Imm4 imm4) { return "ice"; }
|
||||||
std::string arm_SVC(Cond cond, Imm24 imm24) { return "ice"; }
|
std::string arm_SVC(Cond cond, Imm24 imm24) {
|
||||||
std::string arm_UDF() { return "ice"; }
|
return Common::StringFromFormat("svc%s #%u", CondStr(cond), imm24);
|
||||||
|
}
|
||||||
|
std::string arm_UDF() {
|
||||||
|
return Common::StringFromFormat("udf");
|
||||||
|
}
|
||||||
|
|
||||||
// Extension functions
|
// Extension functions
|
||||||
std::string arm_SXTAB(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
|
std::string arm_SXTAB(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
|
||||||
|
|
|
@ -18,6 +18,7 @@ OPCODE(SetCFlag, T::Void, T::U1
|
||||||
OPCODE(GetVFlag, T::U1, )
|
OPCODE(GetVFlag, T::U1, )
|
||||||
OPCODE(SetVFlag, T::Void, T::U1 )
|
OPCODE(SetVFlag, T::Void, T::U1 )
|
||||||
OPCODE(BXWritePC, T::Void, T::U32 )
|
OPCODE(BXWritePC, T::Void, T::U32 )
|
||||||
|
OPCODE(CallSupervisor, T::Void, T::U32 )
|
||||||
|
|
||||||
// Pseudo-operation, handled specially at final emit
|
// Pseudo-operation, handled specially at final emit
|
||||||
OPCODE(GetCarryFromOp, T::U1, T::U32 )
|
OPCODE(GetCarryFromOp, T::U1, T::U32 )
|
||||||
|
|
|
@ -72,6 +72,10 @@ void IREmitter::LoadWritePC(IR::ValuePtr value) {
|
||||||
Inst(IR::Opcode::BXWritePC, {value});
|
Inst(IR::Opcode::BXWritePC, {value});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IREmitter::CallSupervisor(IR::ValuePtr value) {
|
||||||
|
Inst(IR::Opcode::CallSupervisor, {value});
|
||||||
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::GetCFlag() {
|
IR::ValuePtr IREmitter::GetCFlag() {
|
||||||
return Inst(IR::Opcode::GetCFlag, {});
|
return Inst(IR::Opcode::GetCFlag, {});
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ public:
|
||||||
|
|
||||||
void ALUWritePC(IR::ValuePtr value);
|
void ALUWritePC(IR::ValuePtr value);
|
||||||
void LoadWritePC(IR::ValuePtr value);
|
void LoadWritePC(IR::ValuePtr value);
|
||||||
|
void CallSupervisor(IR::ValuePtr value);
|
||||||
|
|
||||||
IR::ValuePtr GetCFlag();
|
IR::ValuePtr GetCFlag();
|
||||||
void SetNFlag(IR::ValuePtr value);
|
void SetNFlag(IR::ValuePtr value);
|
||||||
|
|
|
@ -16,12 +16,22 @@ namespace Arm {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
enum class ConditionalState {
|
||||||
|
/// We haven't met any conditional instructions yet.
|
||||||
|
None,
|
||||||
|
/// Current instruction is a conditional. This marks the end of this basic block.
|
||||||
|
Break,
|
||||||
|
/// This basic block is made up solely of conditional instructions.
|
||||||
|
Translating,
|
||||||
|
};
|
||||||
|
|
||||||
struct ArmTranslatorVisitor final {
|
struct ArmTranslatorVisitor final {
|
||||||
explicit ArmTranslatorVisitor(LocationDescriptor descriptor) : ir(descriptor) {
|
explicit ArmTranslatorVisitor(LocationDescriptor descriptor) : ir(descriptor) {
|
||||||
ASSERT_MSG(!descriptor.TFlag, "The processor must be in Arm mode");
|
ASSERT_MSG(!descriptor.TFlag, "The processor must be in Arm mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
IREmitter ir;
|
IREmitter ir;
|
||||||
|
ConditionalState cond_state = ConditionalState::None;
|
||||||
|
|
||||||
bool TranslateThisInstruction() {
|
bool TranslateThisInstruction() {
|
||||||
ir.SetTerm(IR::Term::Interpret(ir.current_location));
|
ir.SetTerm(IR::Term::Interpret(ir.current_location));
|
||||||
|
@ -33,6 +43,53 @@ struct ArmTranslatorVisitor final {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LinkToNextInstruction() {
|
||||||
|
auto next_location = ir.current_location;
|
||||||
|
next_location.arm_pc += 4;
|
||||||
|
ir.SetTerm(IR::Term::LinkBlock{next_location});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConditionPassed(Cond cond) {
|
||||||
|
ASSERT_MSG(cond_state != ConditionalState::Translating,
|
||||||
|
"In the current impl, ConditionPassed should never be called again once a non-AL cond is hit. "
|
||||||
|
"(i.e.: one and only one conditional instruction per block)");
|
||||||
|
ASSERT_MSG(cond_state != ConditionalState::Break,
|
||||||
|
"This should never happen. We requested a break but that wasn't honored.");
|
||||||
|
ASSERT_MSG(cond != Cond::NV, "NV conditional is obsolete");
|
||||||
|
|
||||||
|
if (cond == Cond::AL) {
|
||||||
|
// Everything is fine with the world
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// non-AL cond
|
||||||
|
|
||||||
|
if (!ir.block.instructions.empty()) {
|
||||||
|
// We've already emitted instructions. Quit for now, we'll make a new block here later.
|
||||||
|
cond_state = ConditionalState::Break;
|
||||||
|
ir.SetTerm(IR::Term::LinkBlock{ir.current_location});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've not emitted instructions yet.
|
||||||
|
// We'll emit one instruction, and set the block-entry conditional appropriately.
|
||||||
|
|
||||||
|
cond_state = ConditionalState::Translating;
|
||||||
|
ir.block.cond = cond;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool arm_SVC(Cond cond, Imm24 imm24) {
|
||||||
|
u32 imm32 = imm24;
|
||||||
|
// SVC<c> #<imm24>
|
||||||
|
if (ConditionPassed(cond)) {
|
||||||
|
ir.CallSupervisor(ir.Imm32(imm32));
|
||||||
|
return LinkToNextInstruction();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool arm_UDF() {
|
bool arm_UDF() {
|
||||||
return TranslateThisInstruction();
|
return TranslateThisInstruction();
|
||||||
}
|
}
|
||||||
|
@ -44,7 +101,7 @@ IR::Block TranslateArm(LocationDescriptor descriptor, MemoryRead32FuncType memor
|
||||||
ArmTranslatorVisitor visitor{descriptor};
|
ArmTranslatorVisitor visitor{descriptor};
|
||||||
|
|
||||||
bool should_continue = true;
|
bool should_continue = true;
|
||||||
while (should_continue) {
|
while (should_continue && visitor.cond_state == ConditionalState::None) {
|
||||||
const u32 arm_pc = visitor.ir.current_location.arm_pc;
|
const u32 arm_pc = visitor.ir.current_location.arm_pc;
|
||||||
const u32 arm_instruction = (*memory_read_32)(arm_pc);
|
const u32 arm_instruction = (*memory_read_32)(arm_pc);
|
||||||
|
|
||||||
|
@ -55,10 +112,21 @@ IR::Block TranslateArm(LocationDescriptor descriptor, MemoryRead32FuncType memor
|
||||||
should_continue = visitor.arm_UDF();
|
should_continue = visitor.arm_UDF();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (visitor.cond_state == ConditionalState::Break) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
visitor.ir.current_location.arm_pc += 4;
|
visitor.ir.current_location.arm_pc += 4;
|
||||||
visitor.ir.block.cycle_count++;
|
visitor.ir.block.cycle_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (visitor.cond_state == ConditionalState::Translating) {
|
||||||
|
if (should_continue) {
|
||||||
|
visitor.ir.SetTerm(IR::Term::LinkBlock{visitor.ir.current_location});
|
||||||
|
}
|
||||||
|
visitor.ir.block.cond_failed = { visitor.ir.current_location };
|
||||||
|
}
|
||||||
|
|
||||||
return visitor.ir.block;
|
return visitor.ir.block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue