From 587fdd3cb7c8a28b294cc1d8841f60e62f84a0f6 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 19 Oct 2019 21:22:09 +0200 Subject: [PATCH 01/21] Add LLVM-style RTTI to Matchers. --- llvm/utils/TableGen/GlobalISelEmitter.cpp | 44 ++++++++++++++++------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp index 026f9ad349444e..2692c53723dcdd 100644 --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -652,7 +652,19 @@ class RuleMatcher; class Matcher { public: + enum MatcherKind { + MK_Group, + MK_Switch, + MK_Rule, + }; +private: + MatcherKind TheKind; +public: + Matcher(MatcherKind Kind) : TheKind(Kind) {} virtual ~Matcher() = default; + + MatcherKind getKind() const { return TheKind; } + virtual void optimize() {} virtual void emit(MatchTable &Table) = 0; @@ -682,6 +694,9 @@ class GroupMatcher final : public Matcher { std::vector> MatcherStorage; public: + GroupMatcher() : Matcher(MK_Group) {} + static bool classof(const Matcher *M) { return M->getKind() == MK_Group; } + /// Add a matcher to the collection of nested matchers if it meets the /// requirements, and return true. If it doesn't, do nothing and return false. /// @@ -741,7 +756,7 @@ class GroupMatcher final : public Matcher { bool candidateConditionMatches(const PredicateMatcher &Predicate) const; }; -class SwitchMatcher : public Matcher { +class SwitchMatcher final : public Matcher { /// All the nested matchers, representing distinct switch-cases. The first /// conditions (as Matcher::getFirstCondition() reports) of all the nested /// matchers must share the same type and path to a value they check, in other @@ -762,6 +777,9 @@ class SwitchMatcher : public Matcher { std::vector> MatcherStorage; public: + SwitchMatcher() : Matcher(MK_Switch) {} + static bool classof(const Matcher *M) { return M->getKind() == MK_Switch; } + bool addMatcher(Matcher &Candidate); void finalize(); @@ -796,7 +814,7 @@ class SwitchMatcher : public Matcher { }; /// Generates code to check that a match rule matches. -class RuleMatcher : public Matcher { +class RuleMatcher final : public Matcher { public: using ActionList = std::list>; using action_iterator = ActionList::iterator; @@ -858,10 +876,12 @@ class RuleMatcher : public Matcher { public: RuleMatcher(ArrayRef SrcLoc) - : Matchers(), Actions(), InsnVariableIDs(), MutatableInsns(), - DefinedOperands(), NextInsnVarID(0), NextOutputInsnID(0), - NextTempRegID(0), SrcLoc(SrcLoc), ComplexSubOperands(), - RuleID(NextRuleID++) {} + : Matcher(MK_Rule), Matchers(), Actions(), InsnVariableIDs(), + MutatableInsns(), DefinedOperands(), NextInsnVarID(0), + NextOutputInsnID(0), NextTempRegID(0), SrcLoc(SrcLoc), + ComplexSubOperands(), RuleID(NextRuleID++) {} + static bool classof(const Matcher *M) { return M->getKind() == MK_Rule; } + RuleMatcher(RuleMatcher &&Other) = default; RuleMatcher &operator=(RuleMatcher &&Other) = default; @@ -5127,8 +5147,8 @@ GlobalISelEmitter::buildMatchTable(MutableArrayRef Rules, std::stable_sort(InputRules.begin(), InputRules.end(), [&OpcodeOrder](const Matcher *A, const Matcher *B) { - auto *L = static_cast(A); - auto *R = static_cast(B); + auto *L = cast(A); + auto *R = cast(B); return std::make_tuple(OpcodeOrder[L->getOpcode()], L->getNumOperands()) < std::make_tuple(OpcodeOrder[R->getOpcode()], @@ -5158,14 +5178,14 @@ void GroupMatcher::optimize() { auto E = Matchers.end(); while (T != E) { while (T != E) { - auto *R = static_cast(*T); + auto *R = cast(*T); if (!R->getFirstConditionAsRootType().get().isValid()) break; ++T; } std::stable_sort(F, T, [](Matcher *A, Matcher *B) { - auto *L = static_cast(A); - auto *R = static_cast(B); + auto *L = cast(A); + auto *R = cast(B); return L->getFirstConditionAsRootType() < R->getFirstConditionAsRootType(); }); @@ -5663,7 +5683,7 @@ void GroupMatcher::emit(MatchTable &Table) { } for (auto &Condition : Conditions) Condition->emitPredicateOpcodes( - Table, *static_cast(*Matchers.begin())); + Table, *cast(*Matchers.begin())); for (const auto &M : Matchers) M->emit(Table); From 0ecf55190dc2c252785af700aafcafa7d81f4032 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 19 Oct 2019 21:21:13 +0200 Subject: [PATCH 02/21] Push cast down to usage. --- llvm/utils/TableGen/GlobalISelEmitter.cpp | 60 +++++++++++------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp index 2692c53723dcdd..80f46a68597481 100644 --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -1116,7 +1116,7 @@ class PredicateMatcher { virtual ~PredicateMatcher() = default; /// Emit MatchTable opcodes that check the predicate for the given operand. virtual void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const = 0; + Matcher& Rule) const = 0; PredicateKind getKind() const { return Kind; } @@ -1180,7 +1180,7 @@ class SameOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override; + Matcher& Rule) const override; bool isIdentical(const PredicateMatcher &B) const override { return OperandPredicateMatcher::isIdentical(B) && @@ -1231,7 +1231,7 @@ class LLTOperandMatcher : public OperandPredicateMatcher { LLTCodeGen getTy() const { return Ty; } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckType") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) << MatchTable::Comment("Type") @@ -1266,7 +1266,7 @@ class PointerToAnyOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckPointerToAny") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) @@ -1297,7 +1297,7 @@ class ComplexPatternOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { unsigned ID = getAllocatedTemporariesBaseID(); Table << MatchTable::Opcode("GIM_CheckComplexPattern") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) @@ -1332,7 +1332,7 @@ class RegisterBankOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckRegBankForClass") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) @@ -1353,7 +1353,7 @@ class MBBOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckIsMBB") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) << MatchTable::LineBreak; @@ -1370,7 +1370,7 @@ class ImmOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckIsImm") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) << MatchTable::LineBreak; @@ -1397,7 +1397,7 @@ class ConstantIntOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckConstantInt") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) @@ -1426,7 +1426,7 @@ class LiteralIntOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckLiteralInt") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) @@ -1454,7 +1454,7 @@ class CmpPredicateOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckCmpPredicate") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) @@ -1484,7 +1484,7 @@ class IntrinsicIDOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckIntrinsicID") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) @@ -1545,7 +1545,7 @@ class OperandMatcher : public PredicateListMatcher { /// Emit MatchTable opcodes that test whether the instruction named in /// InsnVarID matches all the predicates and all the operands. - void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) { + void emitPredicateOpcodes(MatchTable &Table, Matcher& Rule) { if (!Optimized) { std::string Comment; raw_string_ostream CommentOS(Comment); @@ -1695,7 +1695,7 @@ class InstructionOpcodeMatcher : public InstructionPredicateMatcher { bool hasValue() const override { return OpcodeValues.count(I); } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckOpcode") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << getValue() << MatchTable::LineBreak; @@ -1754,7 +1754,7 @@ class InstructionNumOperandsMatcher final : public InstructionPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckNumOperands") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Expected") @@ -1810,7 +1810,7 @@ class InstructionImmPredicateMatcher : public InstructionPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode(getMatchOpcodeForPredicate(Predicate)) << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Predicate") @@ -1851,7 +1851,7 @@ class AtomicOrderingMMOPredicateMatcher : public InstructionPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { StringRef Opcode = "GIM_CheckAtomicOrdering"; if (Comparator == AO_OrStronger) @@ -1887,7 +1887,7 @@ class MemorySizePredicateMatcher : public InstructionPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckMemorySizeEqualTo") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("MMO") << MatchTable::IntValue(MMOIdx) @@ -1918,7 +1918,7 @@ class MemoryAddressSpacePredicateMatcher : public InstructionPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckMemoryAddressSpace") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("MMO") << MatchTable::IntValue(MMOIdx) @@ -1957,7 +1957,7 @@ class MemoryAlignmentPredicateMatcher : public InstructionPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckMemoryAlignment") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("MMO") << MatchTable::IntValue(MMOIdx) @@ -1999,7 +1999,7 @@ class MemoryVsLLTSizePredicateMatcher : public InstructionPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode(Relation == EqualTo ? "GIM_CheckMemorySizeEqualToLLT" : Relation == GreaterThan @@ -2033,7 +2033,7 @@ class GenericInstructionPredicateMatcher : public InstructionPredicateMatcher { .Predicate; } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckCxxInsnPredicate") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("FnId") @@ -2142,7 +2142,7 @@ class InstructionMatcher final : public PredicateListMatcher { /// Emit MatchTable opcodes that test whether the instruction named in /// InsnVarName matches all the predicates and all the operands. - void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) { + void emitPredicateOpcodes(MatchTable &Table, Matcher& Rule) { if (NumOperandsCheck) InstructionNumOperandsMatcher(InsnVarID, getNumOperands()) .emitPredicateOpcodes(Table, Rule); @@ -2255,7 +2255,7 @@ class InstructionOperandMatcher : public OperandPredicateMatcher { InstructionMatcher &getInsnMatcher() const { return *InsnMatcher; } - void emitCaptureOpcodes(MatchTable &Table, RuleMatcher &Rule) const { + void emitCaptureOpcodes(MatchTable &Table, Matcher& Rule) const { const unsigned NewInsnVarID = InsnMatcher->getInsnVarID(); Table << MatchTable::Opcode("GIM_RecordInsn") << MatchTable::Comment("DefineMI") @@ -2267,7 +2267,7 @@ class InstructionOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { emitCaptureOpcodes(Table, Rule); InsnMatcher->emitPredicateOpcodes(Table, Rule); } @@ -3273,9 +3273,10 @@ bool OperandPredicateMatcher::isHigherPriorityThan( } void SameOperandMatcher::emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const { - const OperandMatcher &OtherOM = Rule.getOperandMatcher(MatchingName); - unsigned OtherInsnVarID = Rule.getInsnVarID(OtherOM.getInstructionMatcher()); + Matcher& Rule) const { + RuleMatcher& RM = cast(Rule); + const OperandMatcher &OtherOM = RM.getOperandMatcher(MatchingName); + unsigned OtherInsnVarID = RM.getInsnVarID(OtherOM.getInstructionMatcher()); assert(OtherInsnVarID == OtherOM.getInstructionMatcher().getInsnVarID()); Table << MatchTable::Opcode("GIM_CheckIsSameOperand") @@ -5682,8 +5683,7 @@ void GroupMatcher::emit(MatchTable &Table) { << MatchTable::JumpTarget(LabelID) << MatchTable::LineBreak; } for (auto &Condition : Conditions) - Condition->emitPredicateOpcodes( - Table, *cast(*Matchers.begin())); + Condition->emitPredicateOpcodes(Table, **Matchers.begin()); for (const auto &M : Matchers) M->emit(Table); From 1d81be5a3caf8957b597f05183e93ce5067a6704 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 20 Oct 2019 00:34:50 +0200 Subject: [PATCH 03/21] [TableGen] [GlobalISel] Add better support for register operands. --- llvm/include/llvm/Target/TargetCallingConv.td | 4 + llvm/utils/TableGen/CodeGenDAGPatterns.cpp | 4 +- llvm/utils/TableGen/CodeGenInstruction.cpp | 9 + llvm/utils/TableGen/CodeGenInstruction.h | 3 + llvm/utils/TableGen/CodeGenRegisters.cpp | 3 +- llvm/utils/TableGen/CodeGenTarget.cpp | 18 ++ llvm/utils/TableGen/CodeGenTarget.h | 4 + llvm/utils/TableGen/DAGISelMatcherGen.cpp | 21 +- llvm/utils/TableGen/GlobalISelEmitter.cpp | 272 ++++++++++-------- 9 files changed, 210 insertions(+), 128 deletions(-) diff --git a/llvm/include/llvm/Target/TargetCallingConv.td b/llvm/include/llvm/Target/TargetCallingConv.td index 057f33083e0804..d256ff2797270d 100644 --- a/llvm/include/llvm/Target/TargetCallingConv.td +++ b/llvm/include/llvm/Target/TargetCallingConv.td @@ -82,6 +82,10 @@ class CCIfNest : CCIf<"ArgFlags.isNest()", A> {} /// the specified action. class CCIfSplit : CCIf<"ArgFlags.isSplit()", A> {} +/// CCIfSplitEnd - If this argument is marked with the 'splitend' attribute, apply +/// the specified action. +class CCIfSplitEnd : CCIf<"ArgFlags.isSplitEnd()", A> {} + /// CCIfSRet - If this argument is marked with the 'sret' attribute, apply /// the specified action. class CCIfSRet : CCIf<"ArgFlags.isSRet()", A> {} diff --git a/llvm/utils/TableGen/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/CodeGenDAGPatterns.cpp index 6fdc116721f333..0f6d55115b426d 100644 --- a/llvm/utils/TableGen/CodeGenDAGPatterns.cpp +++ b/llvm/utils/TableGen/CodeGenDAGPatterns.cpp @@ -2487,7 +2487,7 @@ bool TreePatternNode::ApplyTypeConstraints(TreePattern &TP, bool NotRegisters) { // If this is an INSERT_SUBREG, constrain the source and destination VTs to // be the same. if (getOperator()->getName() == "INSERT_SUBREG") { - assert(getChild(0)->getNumTypes() == 1 && "FIXME: Unhandled"); + assert(getChild(0)->getNumTypes() >= 1 && "FIXME: Unhandled"); MadeChange |= UpdateNodeType(0, getChild(0)->getExtType(0), TP); MadeChange |= getChild(0)->UpdateNodeType(0, getExtType(0), TP); } else if (getOperator()->getName() == "REG_SEQUENCE") { @@ -2800,7 +2800,7 @@ TreePatternNodePtr TreePattern::ParseTreePattern(Init *TheInit, ParseTreePattern(Dag->getArg(0), Dag->getArgNameStr(0)); // Apply the type cast. - assert(New->getNumTypes() == 1 && "FIXME: Unhandled"); + assert(New->getNumTypes() >= 1 && "FIXME: Unhandled"); const CodeGenHwModes &CGH = getDAGPatterns().getTargetInfo().getHwModes(); New->UpdateNodeType(0, getValueTypeByHwMode(Operator, CGH), *this); diff --git a/llvm/utils/TableGen/CodeGenInstruction.cpp b/llvm/utils/TableGen/CodeGenInstruction.cpp index 1df5902b081e15..4189c944154e1b 100644 --- a/llvm/utils/TableGen/CodeGenInstruction.cpp +++ b/llvm/utils/TableGen/CodeGenInstruction.cpp @@ -467,6 +467,15 @@ HasOneImplicitDefWithKnownVT(const CodeGenTarget &TargetInfo) const { return MVT::Other; } +/// HasImplicitDef - If the instruction has the passed register as an +/// implicit def. +bool CodeGenInstruction::HasImplicitDef(Record *Reg) const { + assert(Reg->isSubClassOf("Register")); + for (Record *ImplicitDef : ImplicitDefs) + if (ImplicitDef == Reg) + return true; + return false; +} /// FlattenAsmStringVariants - Flatten the specified AsmString to only /// include text from the specified variant, returning the new string. diff --git a/llvm/utils/TableGen/CodeGenInstruction.h b/llvm/utils/TableGen/CodeGenInstruction.h index af851a11676b12..c80f9e0f938a3d 100644 --- a/llvm/utils/TableGen/CodeGenInstruction.h +++ b/llvm/utils/TableGen/CodeGenInstruction.h @@ -301,6 +301,9 @@ template class ArrayRef; MVT::SimpleValueType HasOneImplicitDefWithKnownVT(const CodeGenTarget &TargetInfo) const; + /// HasImplicitDef - If the instruction has the passed register as an + /// implicit def. + bool HasImplicitDef(Record *Reg) const; /// FlattenAsmStringVariants - Flatten the specified AsmString to only /// include text from the specified variant, returning the new string. diff --git a/llvm/utils/TableGen/CodeGenRegisters.cpp b/llvm/utils/TableGen/CodeGenRegisters.cpp index 4584bc7cfae324..790a50c8dbd6ae 100644 --- a/llvm/utils/TableGen/CodeGenRegisters.cpp +++ b/llvm/utils/TableGen/CodeGenRegisters.cpp @@ -1017,7 +1017,8 @@ CodeGenRegisterClass::getMatchingSubClassWithSubRegs( SuperRegRCs.emplace_back(&RC); llvm::stable_sort(SuperRegRCs, SizeOrder); - assert(SuperRegRCs.front() == BiggestSuperRegRC && + assert(SuperRegRCs.front()->getMembers().size() == + BiggestSuperRegRC->getMembers().size() && "Biggest class wasn't first"); // Find all the subreg classes and order them by size too. diff --git a/llvm/utils/TableGen/CodeGenTarget.cpp b/llvm/utils/TableGen/CodeGenTarget.cpp index 31f9f63d168449..6a4095b1cd2dbe 100644 --- a/llvm/utils/TableGen/CodeGenTarget.cpp +++ b/llvm/utils/TableGen/CodeGenTarget.cpp @@ -393,6 +393,24 @@ std::vector CodeGenTarget::getRegisterVTs(Record *R) return Result; } +MVT::SimpleValueType CodeGenTarget::getRegisterKnownVT(Record *R) const { + const CodeGenRegister *Reg = getRegBank().getReg(R); + MVT::SimpleValueType KnownVT = MVT::Other; + for (const auto &RC : getRegBank().getRegClasses()) { + if (RC.contains(Reg)) { + for (auto VT : RC.getValueTypes()) { + if (!VT.isSimple()) + return MVT::Other; + auto SimpleVT = VT.getSimple().SimpleTy; + if (KnownVT == MVT::Other) + KnownVT = SimpleVT; + else if (KnownVT != SimpleVT) + return MVT::Other; + } + } + } + return KnownVT; +} void CodeGenTarget::ReadLegalValueTypes() const { for (const auto &RC : getRegBank().getRegClasses()) diff --git a/llvm/utils/TableGen/CodeGenTarget.h b/llvm/utils/TableGen/CodeGenTarget.h index 6c89f34c50eccf..8eececb38500d2 100644 --- a/llvm/utils/TableGen/CodeGenTarget.h +++ b/llvm/utils/TableGen/CodeGenTarget.h @@ -126,6 +126,10 @@ class CodeGenTarget { /// specified physical register. std::vector getRegisterVTs(Record *R) const; + /// getRegisterKnownVT - If the specified physical register has exactly one + /// SimpleValueType it is returned, otherwise MVT::Other is returned. + MVT::SimpleValueType getRegisterKnownVT(Record *R) const; + ArrayRef getLegalValueTypes() const { if (LegalValueTypes.empty()) ReadLegalValueTypes(); diff --git a/llvm/utils/TableGen/DAGISelMatcherGen.cpp b/llvm/utils/TableGen/DAGISelMatcherGen.cpp index 123ea3374c7482..198cce35c98628 100644 --- a/llvm/utils/TableGen/DAGISelMatcherGen.cpp +++ b/llvm/utils/TableGen/DAGISelMatcherGen.cpp @@ -1051,20 +1051,19 @@ void MatcherGen::EmitResultCode() { // If the pattern also has (implicit) results, count them as well. if (!Pattern.getDstRegs().empty()) { - // If the root came from an implicit def in the instruction handling stuff, - // don't re-add it. - Record *HandledReg = nullptr; const TreePatternNode *DstPat = Pattern.getDstPattern(); - if (!DstPat->isLeaf() &&DstPat->getOperator()->isSubClassOf("Instruction")){ - const CodeGenTarget &CGT = CGP.getTargetInfo(); - CodeGenInstruction &II = CGT.getInstruction(DstPat->getOperator()); - - if (II.HasOneImplicitDefWithKnownVT(CGT) != MVT::Other) - HandledReg = II.ImplicitDefs[0]; - } + const CodeGenTarget &CGT = CGP.getTargetInfo(); + CodeGenInstruction *II = nullptr; + if (!DstPat->isLeaf() && DstPat->getOperator()->isSubClassOf("Instruction")) + II = &CGT.getInstruction(DstPat->getOperator()); for (Record *Reg : Pattern.getDstRegs()) { - if (!Reg->isSubClassOf("Register") || Reg == HandledReg) continue; + if (!Reg->isSubClassOf("Register")) continue; + // If the root came from an implicit def in the instruction handling stuff, + // don't re-add it. + if (II && II->HasImplicitDef(Reg) && + CGT.getRegisterKnownVT(Reg) != MVT::Other) + continue; ++NumSrcResults; } } diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp index 80f46a68597481..4e6e7e0abc7d40 100644 --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -848,7 +848,7 @@ class RuleMatcher final : public Matcher { /// A map of anonymous physical register operands defined by the matchers that /// may be referenced by the renderers. - DenseMap PhysRegOperands; + DenseMap PhysRegDefs, PhysRegUses; /// ID for the next instruction variable defined with implicitlyDefineInsnVar() unsigned NextInsnVarID; @@ -932,9 +932,11 @@ class RuleMatcher final : public Matcher { return make_range(actions_begin(), actions_end()); } + bool hasOperand(StringRef SymbolicName); + void defineOperand(StringRef SymbolicName, OperandMatcher &OM); - void definePhysRegOperand(Record *Reg, OperandMatcher &OM); + void definePhysRegOperand(Record *Reg, OperandMatcher &OM, bool IsDef = false); Error defineComplexSubOperand(StringRef SymbolicName, Record *ComplexPattern, unsigned RendererID, unsigned SubOperandID) { @@ -959,7 +961,8 @@ class RuleMatcher final : public Matcher { InstructionMatcher &getInstructionMatcher(StringRef SymbolicName) const; const OperandMatcher &getOperandMatcher(StringRef Name) const; - const OperandMatcher &getPhysRegOperandMatcher(Record *) const; + const OperandMatcher &getPhysRegOperandMatcher(Record *Reg, + bool IsDef = false) const; void optimize() override; void emit(MatchTable &Table) override; @@ -2392,11 +2395,12 @@ class CopyPhysRegRenderer : public OperandRenderer { protected: unsigned NewInsnID; Record *PhysReg; + bool IsDef; public: - CopyPhysRegRenderer(unsigned NewInsnID, Record *Reg) + CopyPhysRegRenderer(unsigned NewInsnID, Record *Reg, bool IsDef = false) : OperandRenderer(OR_CopyPhysReg), NewInsnID(NewInsnID), - PhysReg(Reg) { + PhysReg(Reg), IsDef(IsDef) { assert(PhysReg); } @@ -2407,14 +2411,14 @@ class CopyPhysRegRenderer : public OperandRenderer { Record *getPhysReg() const { return PhysReg; } void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override { - const OperandMatcher &Operand = Rule.getPhysRegOperandMatcher(PhysReg); + const OperandMatcher &Operand = + Rule.getPhysRegOperandMatcher(PhysReg, IsDef); unsigned OldInsnVarID = Rule.getInsnVarID(Operand.getInstructionMatcher()); Table << MatchTable::Opcode("GIR_Copy") << MatchTable::Comment("NewInsnID") << MatchTable::IntValue(NewInsnID) << MatchTable::Comment("OldInsnID") << MatchTable::IntValue(OldInsnVarID) << MatchTable::Comment("OpIdx") << MatchTable::IntValue(Operand.getOpIdx()) - << MatchTable::Comment(PhysReg->getName()) - << MatchTable::LineBreak; + << MatchTable::Comment(PhysReg->getName()) << MatchTable::LineBreak; } }; @@ -2816,6 +2820,20 @@ class DebugCommentAction : public MatchAction { } }; +/// Generates code to erase an instructiono. +class EraseFromParentAction : public MatchAction { + unsigned InsnID; + +public: + EraseFromParentAction(unsigned InsnID) : InsnID(InsnID) {} + + void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override { + Table << MatchTable::Opcode("GIR_EraseFromParent") + << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID) + << MatchTable::LineBreak; + } +}; + /// Generates code to build an instruction or mutate an existing instruction /// into the desired instruction when this is possible. class BuildMIAction : public MatchAction { @@ -2862,6 +2880,10 @@ class BuildMIAction : public MatchAction { return; } } + // FIXME: This is a hack but it's sufficient for ISel. We'll need to do + // better for combines. Particularly when there are multiple match + // roots. + Rule.addAction(InsnID); } template @@ -2938,14 +2960,6 @@ class BuildMIAction : public MatchAction { Table << MatchTable::NamedValue("GIU_MergeMemOperands_EndOfList") << MatchTable::LineBreak; } - - // FIXME: This is a hack but it's sufficient for ISel. We'll need to do - // better for combines. Particularly when there are multiple match - // roots. - if (InsnID == 0) - Table << MatchTable::Opcode("GIR_EraseFromParent") - << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID) - << MatchTable::LineBreak; } }; @@ -3061,8 +3075,12 @@ unsigned RuleMatcher::getInsnVarID(InstructionMatcher &InsnMatcher) const { llvm_unreachable("Matched Insn was not captured in a local variable"); } +bool RuleMatcher::hasOperand(StringRef SymbolicName) { + return DefinedOperands.find(SymbolicName) != DefinedOperands.end(); +} + void RuleMatcher::defineOperand(StringRef SymbolicName, OperandMatcher &OM) { - if (DefinedOperands.find(SymbolicName) == DefinedOperands.end()) { + if (!hasOperand(SymbolicName)) { DefinedOperands[SymbolicName] = &OM; return; } @@ -3072,7 +3090,9 @@ void RuleMatcher::defineOperand(StringRef SymbolicName, OperandMatcher &OM) { OM.addPredicate(OM.getSymbolicName()); } -void RuleMatcher::definePhysRegOperand(Record *Reg, OperandMatcher &OM) { +void RuleMatcher::definePhysRegOperand(Record *Reg, OperandMatcher &OM, + bool IsDef) { + auto &PhysRegOperands = IsDef ? PhysRegDefs : PhysRegUses; if (PhysRegOperands.find(Reg) == PhysRegOperands.end()) { PhysRegOperands[Reg] = &OM; return; @@ -3089,7 +3109,8 @@ RuleMatcher::getInstructionMatcher(StringRef SymbolicName) const { } const OperandMatcher & -RuleMatcher::getPhysRegOperandMatcher(Record *Reg) const { +RuleMatcher::getPhysRegOperandMatcher(Record *Reg, bool IsDef) const { + auto &PhysRegOperands = IsDef ? PhysRegDefs : PhysRegUses; const auto &I = PhysRegOperands.find(Reg); if (I == PhysRegOperands.end()) { @@ -3291,22 +3312,6 @@ void SameOperandMatcher::emitPredicateOpcodes(MatchTable &Table, //===- GlobalISelEmitter class --------------------------------------------===// -static Expected getInstResultType(const TreePatternNode *Dst) { - ArrayRef ChildTypes = Dst->getExtTypes(); - if (ChildTypes.size() != 1) - return failedImport("Dst pattern child has multiple results"); - - Optional MaybeOpTy; - if (ChildTypes.front().isMachineValueType()) { - MaybeOpTy = - MVTToLLT(ChildTypes.front().getMachineValueType().SimpleTy); - } - - if (!MaybeOpTy) - return failedImport("Dst operand has an unsupported type"); - return *MaybeOpTy; -} - class GlobalISelEmitter { public: explicit GlobalISelEmitter(RecordKeeper &RK); @@ -3344,6 +3349,8 @@ class GlobalISelEmitter { // Rule coverage information. Optional RuleCoverage; + Expected getInstResultType(const TreePatternNode *Dst) const; + void gatherOpcodeValues(); void gatherTypeIDValues(); void gatherNodeEquivs(); @@ -3373,7 +3380,8 @@ class GlobalISelEmitter { Expected createInstructionRenderer(action_iterator InsertPt, RuleMatcher &M, const TreePatternNode *Dst); - void importExplicitDefRenderers(BuildMIAction &DstMIBuilder); + void importExplicitDefRenderers(BuildMIAction &DstMIBuilder, + const TreePatternNode *Src); Expected importExplicitUseRenderers(action_iterator InsertPt, RuleMatcher &M, @@ -3386,9 +3394,8 @@ class GlobalISelEmitter { Error importDefaultOperandRenderers(action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder, DagInit *DefaultOps) const; - Error - importImplicitDefRenderers(BuildMIAction &DstMIBuilder, - const std::vector &ImplicitDefs) const; + Error importImplicitDefRenderers(RuleMatcher &M, BuildMIAction &DstMIBuilder, + const TreePatternNode *Src) const; void emitCxxPredicateFns(raw_ostream &OS, StringRef CodeFieldName, StringRef TypeIdentifier, StringRef ArgType, @@ -3466,6 +3473,24 @@ class GlobalISelEmitter { std::vector> &MatcherStorage); }; +Expected +GlobalISelEmitter::getInstResultType(const TreePatternNode *Dst) const { + ArrayRef ChildTypes = Dst->getExtTypes(); + CodeGenInstruction *DstI = &Target.getInstruction(Dst->getOperator()); + if (ChildTypes.size() - DstI->ImplicitDefs.size() != 1) + return failedImport("Dst pattern child has multiple results"); + + Optional MaybeOpTy; + if (ChildTypes.front().isMachineValueType()) { + MaybeOpTy = + MVTToLLT(ChildTypes.front().getMachineValueType().SimpleTy); + } + + if (!MaybeOpTy) + return failedImport("Dst operand has an unsupported type"); + return *MaybeOpTy; +} + void GlobalISelEmitter::gatherOpcodeValues() { InstructionOpcodeMatcher::initOpcodeValuesMap(Target); } @@ -3903,6 +3928,7 @@ Error GlobalISelEmitter::importChildMatcher( return Error::success(); } if (SrcChild->getOperator()->getName() == "timm") { + return failedImport("I don't think this works"); OM.addPredicate(); return Error::success(); } @@ -4033,6 +4059,9 @@ Error GlobalISelEmitter::importChildMatcher( Expected GlobalISelEmitter::importExplicitUseRenderer( action_iterator InsertPt, RuleMatcher &Rule, BuildMIAction &DstMIBuilder, TreePatternNode *DstChild) { + if (DstChild->hasName() && !Rule.hasOperand(DstChild->getName())) + return failedImport("Could not find any uses of operand " + + DstChild->getName() + " in the pattern"); const auto &SubOperand = Rule.getComplexSubOperand(DstChild->getName()); if (SubOperand.hasValue()) { @@ -4199,11 +4228,18 @@ Expected GlobalISelEmitter::createAndImportInstructionRenderer( CopyToPhysRegMIBuilder.addRenderer(PhysInput.first); } - importExplicitDefRenderers(DstMIBuilder); + importExplicitDefRenderers(DstMIBuilder, Src); if (auto Error = importExplicitUseRenderers(InsertPt, M, DstMIBuilder, Dst) .takeError()) return std::move(Error); + InsertPt = InsertPtOrError.get(); + + // Render the implicit defs. + // These are only added to the root of the result. + if (auto Error = importImplicitDefRenderers(M, DstMIBuilder, Src)) + return std::move(Error); + InsertPt = InsertPtOrError.get(); return DstMIBuilder; } @@ -4352,9 +4388,10 @@ Expected GlobalISelEmitter::createInstructionRenderer( } void GlobalISelEmitter::importExplicitDefRenderers( - BuildMIAction &DstMIBuilder) { + BuildMIAction &DstMIBuilder, const llvm::TreePatternNode *Src) { const CodeGenInstruction *DstI = DstMIBuilder.getCGI(); - for (unsigned I = 0; I < DstI->Operands.NumDefs; ++I) { + unsigned NumDefs = std::min(DstI->Operands.NumDefs, Src->getNumResults()); + for (unsigned I = 0; I < NumDefs; ++I) { const CGIOperandList::OperandInfo &DstIOperand = DstI->Operands[I]; DstMIBuilder.addRenderer(DstIOperand.Name); } @@ -4574,10 +4611,22 @@ Error GlobalISelEmitter::importDefaultOperandRenderers( } Error GlobalISelEmitter::importImplicitDefRenderers( - BuildMIAction &DstMIBuilder, - const std::vector &ImplicitDefs) const { - if (!ImplicitDefs.empty()) - return failedImport("Pattern defines a physical register"); + RuleMatcher &M, BuildMIAction &DstMIBuilder, + const TreePatternNode *Src) const { + const CodeGenInstruction &DstI = *DstMIBuilder.getCGI(); + int DstINumImplicitDefs = Src->getNumResults() - DstI.Operands.NumDefs; + + for (int I = 0; I < DstINumImplicitDefs; ++I) { + auto PhysOutput = DstI.ImplicitDefs[I]; + assert(PhysOutput->isSubClassOf("Register")); + BuildMIAction &CopyFromPhysRegMIBuilder = M.addAction( + M.allocateOutputInsnID(), &Target.getInstruction(RK.getDef("COPY"))); + CopyFromPhysRegMIBuilder.addRenderer(PhysOutput, true); + CopyFromPhysRegMIBuilder.addRenderer(PhysOutput); + M.addAction( + CopyFromPhysRegMIBuilder.getInsnID(), 0, + *CGRegs.getRegClassForRegister(PhysOutput)); + } return Error::success(); } @@ -4793,75 +4842,75 @@ Expected GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { auto &DstI = Target.getInstruction(DstOp); StringRef DstIName = DstI.TheDef->getName(); - if (DstI.Operands.NumDefs != Src->getExtTypes().size()) - return failedImport("Src pattern results and dst MI defs are different (" + - to_string(Src->getExtTypes().size()) + " def(s) vs " + - to_string(DstI.Operands.NumDefs) + " def(s))"); + unsigned SrcNumResults = Src->getNumResults(); + unsigned DstNumDefs = DstI.Operands.NumDefs + DstI.ImplicitDefs.size(); + if (SrcNumResults > DstNumDefs) + return failedImport("More src pattern results than dst MI defs (" + + to_string(SrcNumResults) + " result(s) > " + + to_string(DstNumDefs) + " def(s))"); // The root of the match also has constraints on the register bank so that it // matches the result instruction. - unsigned OpIdx = 0; - for (const TypeSetByHwMode &VTy : Src->getExtTypes()) { - (void)VTy; - - const auto &DstIOperand = DstI.Operands[OpIdx]; - Record *DstIOpRec = DstIOperand.Rec; - if (DstIName == "COPY_TO_REGCLASS") { - DstIOpRec = getInitValueAsRegClass(Dst->getChild(1)->getLeafValue()); - - if (DstIOpRec == nullptr) - return failedImport( - "COPY_TO_REGCLASS operand #1 isn't a register class"); - } else if (DstIName == "REG_SEQUENCE") { - DstIOpRec = getInitValueAsRegClass(Dst->getChild(0)->getLeafValue()); - if (DstIOpRec == nullptr) - return failedImport("REG_SEQUENCE operand #0 isn't a register class"); - } else if (DstIName == "EXTRACT_SUBREG") { - auto InferredClass = inferRegClassFromPattern(Dst->getChild(0)); - if (!InferredClass) - return failedImport("Could not infer class for EXTRACT_SUBREG operand #0"); - - // We can assume that a subregister is in the same bank as it's super - // register. - DstIOpRec = (*InferredClass)->getDef(); - } else if (DstIName == "INSERT_SUBREG") { - auto MaybeSuperClass = inferSuperRegisterClassForNode( - VTy, Dst->getChild(0), Dst->getChild(2)); - if (!MaybeSuperClass) - return failedImport( - "Cannot infer register class for INSERT_SUBREG operand #0"); - // Move to the next pattern here, because the register class we found - // doesn't necessarily have a record associated with it. So, we can't - // set DstIOpRec using this. - OperandMatcher &OM = InsnMatcher.getOperand(OpIdx); - OM.setSymbolicName(DstIOperand.Name); - M.defineOperand(OM.getSymbolicName(), OM); - OM.addPredicate(**MaybeSuperClass); - ++OpIdx; - continue; - } else if (DstIName == "SUBREG_TO_REG") { - auto MaybeRegClass = inferSuperRegisterClass(VTy, Dst->getChild(2)); - if (!MaybeRegClass) - return failedImport( - "Cannot infer register class for SUBREG_TO_REG operand #0"); - OperandMatcher &OM = InsnMatcher.getOperand(OpIdx); + for (unsigned OpIdx = 0; OpIdx != SrcNumResults; ++OpIdx) { + const auto &VTy = Src->getExtType(OpIdx); + OperandMatcher &OM = InsnMatcher.getOperand(OpIdx); + Record *DstIOpRec; + const CodeGenRegisterClass *RC = nullptr; + if (OpIdx < DstI.Operands.NumDefs) { + const auto &DstIOperand = DstI.Operands[OpIdx]; OM.setSymbolicName(DstIOperand.Name); M.defineOperand(OM.getSymbolicName(), OM); - OM.addPredicate(**MaybeRegClass); - ++OpIdx; - continue; - } else if (DstIOpRec->isSubClassOf("RegisterOperand")) - DstIOpRec = DstIOpRec->getValueAsDef("RegClass"); - else if (!DstIOpRec->isSubClassOf("RegisterClass")) - return failedImport("Dst MI def isn't a register class" + - to_string(*Dst)); + DstIOpRec = DstIOperand.Rec; + + if (DstIName == "COPY_TO_REGCLASS") { + DstIOpRec = getInitValueAsRegClass(Dst->getChild(1)->getLeafValue()); + + if (DstIOpRec == nullptr) + return failedImport( + "COPY_TO_REGCLASS operand #1 isn't a register class"); + } else if (DstIName == "REG_SEQUENCE") { + DstIOpRec = getInitValueAsRegClass(Dst->getChild(0)->getLeafValue()); + if (DstIOpRec == nullptr) + return failedImport("REG_SEQUENCE operand #0 isn't a register class"); + } else if (DstIName == "EXTRACT_SUBREG") { + auto InferredClass = inferRegClassFromPattern(Dst->getChild(0)); + if (!InferredClass) + return failedImport("Could not infer class for EXTRACT_SUBREG operand #0"); + + // We can assume that a subregister is in the same bank as it's super + // register. + DstIOpRec = (*InferredClass)->getDef(); + } else if (DstIName == "INSERT_SUBREG") { + auto MaybeSuperClass = inferSuperRegisterClassForNode( + VTy, Dst->getChild(0), Dst->getChild(2)); + if (!MaybeSuperClass) + return failedImport( + "Cannot infer register class for INSERT_SUBREG operand #0"); + RC = *MaybeSuperClass; + } else if (DstIName == "SUBREG_TO_REG") { + auto MaybeRegClass = inferSuperRegisterClass(VTy, Dst->getChild(2)); + if (!MaybeRegClass) + return failedImport( + "Cannot infer register class for SUBREG_TO_REG operand #0"); + RC = *MaybeRegClass; + } else if (DstIOpRec->isSubClassOf("RegisterOperand")) + DstIOpRec = DstIOpRec->getValueAsDef("RegClass"); + else if (!DstIOpRec->isSubClassOf("RegisterClass")) + return failedImport("Dst MI def isn't a register class" + + to_string(*Dst)); + } else { + DstIOpRec = DstI.ImplicitDefs[OpIdx - DstI.Operands.NumDefs]; + if (!DstIOpRec->isSubClassOf("Register")) + return failedImport("Expected implicit def to be a register."); + RC = CGRegs.getRegClassForRegister(DstIOpRec); + if (!RC) + return failedImport("Cannot infer register class for register"); + M.definePhysRegOperand(DstIOpRec, OM, true); + } - OperandMatcher &OM = InsnMatcher.getOperand(OpIdx); - OM.setSymbolicName(DstIOperand.Name); - M.defineOperand(OM.getSymbolicName(), OM); - OM.addPredicate( - Target.getRegisterClass(DstIOpRec)); - ++OpIdx; + if (!RC) + RC = &Target.getRegisterClass(DstIOpRec); + OM.addPredicate(*RC); } auto DstMIBuilderOrError = @@ -4870,11 +4919,6 @@ Expected GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { return std::move(Error); BuildMIAction &DstMIBuilder = DstMIBuilderOrError.get(); - // Render the implicit defs. - // These are only added to the root of the result. - if (auto Error = importImplicitDefRenderers(DstMIBuilder, P.getDstRegs())) - return std::move(Error); - DstMIBuilder.chooseInsnToMutate(M); // Constrain the registers to classes. This is normally derived from the From 218e7cf542390fdb8d89f568ac2feacaaca51b65 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 27 Oct 2019 16:38:38 +0100 Subject: [PATCH 04/21] [GlobalISel] Use correct calling convention from callee. --- .../include/llvm/CodeGen/GlobalISel/CallLowering.h | 3 ++- llvm/lib/CodeGen/GlobalISel/CallLowering.cpp | 9 +++++---- .../Target/AArch64/GISel/AArch64CallLowering.cpp | 14 +++++++++----- llvm/lib/Target/AMDGPU/AMDGPUCallLowering.cpp | 2 +- llvm/lib/Target/ARM/ARMCallLowering.cpp | 12 ++++++++---- llvm/lib/Target/X86/X86CallLowering.cpp | 12 ++++++++---- 6 files changed, 33 insertions(+), 19 deletions(-) diff --git a/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h b/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h index 88a1837665aab6..678cf129e9e5d5 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h @@ -218,7 +218,8 @@ class CallLowering { /// \p Callback to move them to the assigned locations. /// /// \return True if everything has succeeded, false otherwise. - bool handleAssignments(MachineIRBuilder &MIRBuilder, + bool handleAssignments(CallingConv::ID CallConv, bool IsVarArg, + MachineIRBuilder &MIRBuilder, SmallVectorImpl &Args, ValueHandler &Handler) const; bool handleAssignments(CCState &CCState, diff --git a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp index 5bcc242d3528eb..2e9f50a451d7fc 100644 --- a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp +++ b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp @@ -173,13 +173,14 @@ void CallLowering::unpackRegs(ArrayRef DstRegs, Register SrcReg, MIRBuilder.buildExtract(DstRegs[i], SrcReg, Offsets[i]); } -bool CallLowering::handleAssignments(MachineIRBuilder &MIRBuilder, +bool CallLowering::handleAssignments(CallingConv::ID CC, bool isVarArg, + MachineIRBuilder &MIRBuilder, SmallVectorImpl &Args, ValueHandler &Handler) const { MachineFunction &MF = MIRBuilder.getMF(); const Function &F = MF.getFunction(); SmallVector ArgLocs; - CCState CCInfo(F.getCallingConv(), F.isVarArg(), MF, ArgLocs, F.getContext()); + CCState CCInfo(CC, isVarArg, MF, ArgLocs, F.getContext()); return handleAssignments(CCInfo, ArgLocs, MIRBuilder, Args, Handler); } @@ -200,12 +201,12 @@ bool CallLowering::handleAssignments(CCState &CCInfo, if (!CurVT.isValid()) return false; MVT NewVT = TLI->getRegisterTypeForCallingConv( - F.getContext(), F.getCallingConv(), EVT(CurVT)); + F.getContext(), CCInfo.getCallingConv(), EVT(CurVT)); // If we need to split the type over multiple regs, check it's a scenario // we currently support. unsigned NumParts = TLI->getNumRegistersForCallingConv( - F.getContext(), F.getCallingConv(), CurVT); + F.getContext(), CCInfo.getCallingConv(), CurVT); if (NumParts > 1) { // For now only handle exact splits. if (NewVT.getSizeInBits() * NumParts != CurVT.getSizeInBits()) diff --git a/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp b/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp index 7903299fcc33cc..b85e0e8c40873f 100644 --- a/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp @@ -372,7 +372,8 @@ bool AArch64CallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, } OutgoingArgHandler Handler(MIRBuilder, MRI, MIB, AssignFn, AssignFn); - Success = handleAssignments(MIRBuilder, SplitArgs, Handler); + Success = + handleAssignments(CC, F.isVarArg(), MIRBuilder, SplitArgs, Handler); } if (SwiftErrorVReg) { @@ -459,7 +460,8 @@ bool AArch64CallLowering::lowerFormalArguments( TLI.CCAssignFnForCall(F.getCallingConv(), /*IsVarArg=*/false); FormalArgHandler Handler(MIRBuilder, MRI, AssignFn); - if (!handleAssignments(MIRBuilder, SplitArgs, Handler)) + if (!handleAssignments(F.getCallingConv(), F.isVarArg(), MIRBuilder, + SplitArgs, Handler)) return false; AArch64FunctionInfo *FuncInfo = MF.getInfo(); @@ -883,7 +885,7 @@ bool AArch64CallLowering::lowerTailCall( SmallVector PhysRegs; OutgoingArgHandler Handler(MIRBuilder, MRI, MIB, AssignFnFixed, AssignFnVarArg, true, FPDiff); - if (!handleAssignments(MIRBuilder, OutArgs, Handler)) + if (!handleAssignments(CalleeCC, Info.IsVarArg, MIRBuilder, OutArgs, Handler)) return false; if (Info.IsVarArg && Info.IsMustTailCall) { @@ -1001,7 +1003,8 @@ bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, SmallVector PhysRegs; OutgoingArgHandler Handler(MIRBuilder, MRI, MIB, AssignFnFixed, AssignFnVarArg, false); - if (!handleAssignments(MIRBuilder, OutArgs, Handler)) + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, OutArgs, + Handler)) return false; // Now we can add the actual call instruction to the correct basic block. @@ -1022,7 +1025,8 @@ bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, if (!Info.OrigRet.Ty->isVoidTy()) { CCAssignFn *RetAssignFn = TLI.CCAssignFnForReturn(Info.CallConv); CallReturnHandler Handler(MIRBuilder, MRI, MIB, RetAssignFn); - if (!handleAssignments(MIRBuilder, InArgs, Handler)) + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, InArgs, + Handler)) return false; } diff --git a/llvm/lib/Target/AMDGPU/AMDGPUCallLowering.cpp b/llvm/lib/Target/AMDGPU/AMDGPUCallLowering.cpp index b929a3c60a3770..1a9692bd6c5e78 100644 --- a/llvm/lib/Target/AMDGPU/AMDGPUCallLowering.cpp +++ b/llvm/lib/Target/AMDGPU/AMDGPUCallLowering.cpp @@ -346,7 +346,7 @@ bool AMDGPUCallLowering::lowerReturnVal(MachineIRBuilder &B, CCAssignFn *AssignFn = TLI.CCAssignFnForReturn(CC, F.isVarArg()); OutgoingValueHandler RetHandler(B, *MRI, Ret, AssignFn); - return handleAssignments(B, SplitRetInfos, RetHandler); + return handleAssignments(CC, F.isVarArg(), B, SplitRetInfos, RetHandler); } bool AMDGPUCallLowering::lowerReturn(MachineIRBuilder &B, diff --git a/llvm/lib/Target/ARM/ARMCallLowering.cpp b/llvm/lib/Target/ARM/ARMCallLowering.cpp index 4fbb3b6993e4e3..24ba9dfa44942b 100644 --- a/llvm/lib/Target/ARM/ARMCallLowering.cpp +++ b/llvm/lib/Target/ARM/ARMCallLowering.cpp @@ -259,7 +259,8 @@ bool ARMCallLowering::lowerReturnVal(MachineIRBuilder &MIRBuilder, TLI.CCAssignFnForReturn(F.getCallingConv(), F.isVarArg()); OutgoingValueHandler RetHandler(MIRBuilder, MF.getRegInfo(), Ret, AssignFn); - return handleAssignments(MIRBuilder, SplitRetInfos, RetHandler); + return handleAssignments(F.getCallingConv(), F.isVarArg(), MIRBuilder, + SplitRetInfos, RetHandler); } bool ARMCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, @@ -459,7 +460,8 @@ bool ARMCallLowering::lowerFormalArguments( if (!MBB.empty()) MIRBuilder.setInstr(*MBB.begin()); - if (!handleAssignments(MIRBuilder, SplitArgInfos, ArgHandler)) + if (!handleAssignments(F.getCallingConv(), F.isVarArg(), MIRBuilder, + SplitArgInfos, ArgHandler)) return false; // Move back to the end of the basic block. @@ -555,7 +557,8 @@ bool ARMCallLowering::lowerCall(MachineIRBuilder &MIRBuilder, CallLoweringInfo & auto ArgAssignFn = TLI.CCAssignFnForCall(Info.CallConv, IsVarArg); OutgoingValueHandler ArgHandler(MIRBuilder, MRI, MIB, ArgAssignFn); - if (!handleAssignments(MIRBuilder, ArgInfos, ArgHandler)) + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, ArgInfos, + ArgHandler)) return false; // Now we can add the actual call instruction to the correct basic block. @@ -569,7 +572,8 @@ bool ARMCallLowering::lowerCall(MachineIRBuilder &MIRBuilder, CallLoweringInfo & splitToValueTypes(Info.OrigRet, ArgInfos, MF); auto RetAssignFn = TLI.CCAssignFnForReturn(Info.CallConv, IsVarArg); CallReturnHandler RetHandler(MIRBuilder, MRI, MIB, RetAssignFn); - if (!handleAssignments(MIRBuilder, ArgInfos, RetHandler)) + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, ArgInfos, + RetHandler)) return false; } diff --git a/llvm/lib/Target/X86/X86CallLowering.cpp b/llvm/lib/Target/X86/X86CallLowering.cpp index 319dc947060480..4501b9ba34f621 100644 --- a/llvm/lib/Target/X86/X86CallLowering.cpp +++ b/llvm/lib/Target/X86/X86CallLowering.cpp @@ -216,7 +216,8 @@ bool X86CallLowering::lowerReturn( } OutgoingValueHandler Handler(MIRBuilder, MRI, MIB, RetCC_X86); - if (!handleAssignments(MIRBuilder, SplitArgs, Handler)) + if (!handleAssignments(F.getCallingConv(), F.isVarArg(), MIRBuilder, + SplitArgs, Handler)) return false; } @@ -366,7 +367,8 @@ bool X86CallLowering::lowerFormalArguments( MIRBuilder.setInstr(*MBB.begin()); FormalArgHandler Handler(MIRBuilder, MRI, CC_X86); - if (!handleAssignments(MIRBuilder, SplitArgs, Handler)) + if (!handleAssignments(F.getCallingConv(), F.isVarArg(), MIRBuilder, + SplitArgs, Handler)) return false; // Move back to the end of the basic block. @@ -422,7 +424,8 @@ bool X86CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, } // Do the actual argument marshalling. OutgoingValueHandler Handler(MIRBuilder, MRI, MIB, CC_X86); - if (!handleAssignments(MIRBuilder, SplitArgs, Handler)) + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, SplitArgs, + Handler)) return false; bool IsFixed = Info.OrigArgs.empty() ? true : Info.OrigArgs.back().IsFixed; @@ -471,7 +474,8 @@ bool X86CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, return false; CallReturnHandler Handler(MIRBuilder, MRI, RetCC_X86, MIB); - if (!handleAssignments(MIRBuilder, SplitArgs, Handler)) + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, SplitArgs, + Handler)) return false; if (!NewRegs.empty()) From 2026d1782ef6c876489b3b497998766c9127bc0f Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 27 Oct 2019 16:41:54 +0100 Subject: [PATCH 05/21] [GlobalISel] Improve Legalizer. --- .../GlobalISel/LegalizationArtifactCombiner.h | 111 ++- .../llvm/CodeGen/GlobalISel/LegalizerHelper.h | 20 +- .../llvm/CodeGen/GlobalISel/LegalizerInfo.h | 12 +- llvm/include/llvm/IR/RuntimeLibcalls.def | 5 + llvm/lib/CodeGen/GlobalISel/Legalizer.cpp | 36 +- .../CodeGen/GlobalISel/LegalizerHelper.cpp | 700 +++++++++++------- 6 files changed, 551 insertions(+), 333 deletions(-) diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h index 016b0bacab8527..f31c88131cf3ec 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h @@ -651,17 +651,6 @@ class LegalizationArtifactCombiner { return true; } - static bool isMergeLikeOpcode(unsigned Opc) { - switch (Opc) { - case TargetOpcode::G_MERGE_VALUES: - case TargetOpcode::G_BUILD_VECTOR: - case TargetOpcode::G_CONCAT_VECTORS: - return true; - default: - return false; - } - } - bool tryCombineExtract(MachineInstr &MI, SmallVectorImpl &DeadInsts, SmallVectorImpl &UpdatedDefs) { @@ -680,34 +669,94 @@ class LegalizationArtifactCombiner { // %3 = G_EXTRACT %1, (N - %0.getSizeInBits() Register SrcReg = lookThroughCopyInstrs(MI.getOperand(1).getReg()); - MachineInstr *MergeI = MRI.getVRegDef(SrcReg); - if (!MergeI || !isMergeLikeOpcode(MergeI->getOpcode())) + MachineInstr *SrcI = MRI.getVRegDef(SrcReg); + if (!SrcI) return false; Register DstReg = MI.getOperand(0).getReg(); - LLT DstTy = MRI.getType(DstReg); - LLT SrcTy = MRI.getType(SrcReg); + LLT ExtractTy = MRI.getType(DstReg); + unsigned ExtractSize = ExtractTy.getSizeInBits(); + LLT ExtractSrcTy = MRI.getType(SrcReg); + unsigned ExtractSrcSize = ExtractSrcTy.getSizeInBits(); + unsigned ExtractOffset = MI.getOperand(2).getImm(); - // TODO: Do we need to check if the resulting extract is supported? - unsigned ExtractDstSize = DstTy.getSizeInBits(); - unsigned Offset = MI.getOperand(2).getImm(); - unsigned NumMergeSrcs = MergeI->getNumOperands() - 1; - unsigned MergeSrcSize = SrcTy.getSizeInBits() / NumMergeSrcs; - unsigned MergeSrcIdx = Offset / MergeSrcSize; - - // Compute the offset of the last bit the extract needs. - unsigned EndMergeSrcIdx = (Offset + ExtractDstSize - 1) / MergeSrcSize; + Builder.setInstr(MI); + switch (SrcI->getOpcode()) { + case TargetOpcode::G_IMPLICIT_DEF: + Builder.buildUndef(DstReg); + break; + case TargetOpcode::G_INSERT: { + // Try to look through insert + // + // %2 = G_INSERT %1, %0, N0 + // %3 = G_EXTRACT %2, N1 + // => + // + // if N0 == N1 && %0.size == %3.size + // %3 = COPY %0 + // else if N0 <= N1 && N0 + %0.size >= N1 + %3.size + // %3 = G_EXTRACT %0, (N1 - N0) + // else if N0 >= N1 + %3.size || N0 + %0.size >= N1 + // %3 = G_EXTRACT %1, N1 + + Register InsertSrcReg = SrcI->getOperand(1).getReg(); + Register InsertReg = SrcI->getOperand(2).getReg(); + LLT InsertTy = MRI.getType(InsertReg); + unsigned InsertSize = InsertTy.getSizeInBits(); + unsigned InsertOffset = SrcI->getOperand(3).getImm(); + if (InsertOffset == ExtractOffset && InsertSize == ExtractSize) + // intervals are equal, use inserted value directly + Builder.buildCopy(DstReg, InsertReg); + else if (InsertOffset <= ExtractOffset && + InsertOffset + InsertSize >= ExtractOffset + ExtractSize) + // extract interval subset of insert interval + // extract from inserted value instead + Builder.buildExtract(DstReg, InsertReg, ExtractOffset - InsertOffset); + else if (InsertOffset >= ExtractOffset + ExtractSize || + InsertOffset + InsertSize >= ExtractOffset) + // intervals are disjoint, extract from insert source instead + Builder.buildExtract(DstReg, InsertSrcReg, ExtractOffset); + else + // Can't handle the case where the extract spans insert source and value + return false; + break; + } + case TargetOpcode::G_MERGE_VALUES: + case TargetOpcode::G_BUILD_VECTOR: + case TargetOpcode::G_CONCAT_VECTORS: { + // TODO: Do we need to check if the resulting extract is supported? + unsigned NumMergeSrcs = SrcI->getNumOperands() - 1; + unsigned MergeSrcSize = ExtractSrcSize / NumMergeSrcs; + unsigned MergeSrcIdx = ExtractOffset / MergeSrcSize; + + // Compute the offset of the last bit the extract needs. + unsigned EndMergeSrcIdx = (ExtractOffset + ExtractSize - 1) / MergeSrcSize; + + Register MergeSrcReg; + if (MergeSrcIdx != EndMergeSrcIdx) { + // Create a sub-merge to extract from. + unsigned NumSubSrcs = EndMergeSrcIdx - MergeSrcIdx + 1; + SmallVector SubRegs; + SubRegs.reserve(NumSubSrcs); + for (unsigned Idx = MergeSrcIdx; Idx <= EndMergeSrcIdx; ++Idx) + SubRegs.push_back(SrcI->getOperand(Idx + 1).getReg()); + auto SubMerge = + Builder.buildMerge(LLT::scalar(MergeSrcSize * NumSubSrcs), SubRegs); + MergeSrcReg = SubMerge->getOperand(0).getReg(); + } else + MergeSrcReg = SrcI->getOperand(MergeSrcIdx + 1).getReg(); - // Can't handle the case where the extract spans multiple inputs. - if (MergeSrcIdx != EndMergeSrcIdx) + // TODO: We could modify MI in place in most cases. + Builder.buildExtract(DstReg, SrcI->getOperand(MergeSrcIdx + 1).getReg(), + ExtractOffset - MergeSrcIdx * MergeSrcSize); + break; + } + default: return false; + } - // TODO: We could modify MI in place in most cases. - Builder.setInstr(MI); - Builder.buildExtract(DstReg, MergeI->getOperand(MergeSrcIdx + 1).getReg(), - Offset - MergeSrcIdx * MergeSrcSize); UpdatedDefs.push_back(DstReg); - markInstAndDefDead(MI, *MergeI, DeadInsts); + markInstAndDefDead(MI, *SrcI, DeadInsts); return true; } diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h index 0fe1d60b630db2..175a68651f6d6f 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h @@ -94,7 +94,7 @@ class LegalizerHelper { /// Legalize an instruction by splitting it into simpler parts, hopefully /// understood by the target. - LegalizeResult lower(MachineInstr &MI, unsigned TypeIdx, LLT Ty); + LegalizeResult lower(MachineInstr &MI, unsigned TypeIdx); /// Legalize a vector instruction by splitting into multiple components, each /// acting on the same scalar type as the original but with fewer elements. @@ -172,9 +172,8 @@ class LegalizerHelper { /// Version which handles irregular splits. bool extractParts(Register Reg, LLT RegTy, LLT MainTy, - LLT &LeftoverTy, - SmallVectorImpl &VRegs, - SmallVectorImpl &LeftoverVRegs); + SmallVectorImpl &VRegs, LLT &LeftoverTy, + Register &LeftoverReg); /// Helper function to build a wide generic register \p DstReg of type \p /// RegTy from smaller parts. This will produce a G_MERGE_VALUES, @@ -185,9 +184,9 @@ class LegalizerHelper { /// /// If \p ResultTy does not evenly break into \p PartTy sized pieces, the /// remainder must be specified with \p LeftoverRegs of type \p LeftoverTy. - void insertParts(Register DstReg, LLT ResultTy, - LLT PartTy, ArrayRef PartRegs, - LLT LeftoverTy = LLT(), ArrayRef LeftoverRegs = {}); + void insertParts(Register DstReg, LLT ResultTy, LLT PartTy, + ArrayRef PartRegs, LLT LeftoverTy = LLT(), + Register LeftoverReg = Register()); /// Unmerge \p SrcReg into \p Parts with the greatest common divisor type with /// \p DstTy and \p NarrowTy. Returns the GCD type. @@ -279,10 +278,13 @@ class LegalizerHelper { LegalizeResult narrowScalarShift(MachineInstr &MI, unsigned TypeIdx, LLT Ty); LegalizeResult narrowScalarMul(MachineInstr &MI, LLT Ty); - LegalizeResult narrowScalarExtract(MachineInstr &MI, unsigned TypeIdx, LLT Ty); + LegalizeResult narrowScalarExtract(MachineInstr &MI, unsigned TypeIdx, + LLT Ty); LegalizeResult narrowScalarInsert(MachineInstr &MI, unsigned TypeIdx, LLT Ty); LegalizeResult narrowScalarBasic(MachineInstr &MI, unsigned TypeIdx, LLT Ty); + LegalizeResult narrowScalarExtended(MachineInstr &MI, unsigned TypeIdx, + LLT Ty); LegalizeResult narrowScalarExt(MachineInstr &MI, unsigned TypeIdx, LLT Ty); LegalizeResult narrowScalarSelect(MachineInstr &MI, unsigned TypeIdx, LLT Ty); LegalizeResult narrowScalarCTLZ(MachineInstr &MI, unsigned TypeIdx, LLT Ty); @@ -314,6 +316,8 @@ class LegalizerHelper { LegalizeResult lowerExtract(MachineInstr &MI); LegalizeResult lowerInsert(MachineInstr &MI); LegalizeResult lowerSADDO_SSUBO(MachineInstr &MI); + LegalizeResult lowerUADDSAT_USUBSAT(MachineInstr &MI); + LegalizeResult lowerSADDSAT_SSUBSAT(MachineInstr &MI); LegalizeResult lowerBswap(MachineInstr &MI); LegalizeResult lowerBitreverse(MachineInstr &MI); LegalizeResult lowerReadWriteRegister(MachineInstr &MI); diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h index 61e0418757bce0..53838488e350a4 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h @@ -20,11 +20,11 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/TargetOpcodes.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/LowLevelTypeImpl.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/LowLevelTypeImpl.h" #include #include #include @@ -1216,6 +1216,14 @@ class LegalizerInfo { MachineInstr &MI) const { llvm_unreachable("must implement this if custom action is used"); } + virtual LegalizerHelper::LegalizeResult + legalizeCustom(MachineInstr &MI, MachineRegisterInfo &MRI, + MachineIRBuilder &MIRBuilder, GISelChangeObserver &Observer, + LegalizerHelper &Helper) const { + return legalizeCustom(Helper, MI) + ? LegalizerHelper::Legalized + : LegalizerHelper::UnableToLegalize; + } /// \returns true if MI is either legal or has been legalized and false if not /// legal. diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.def b/llvm/include/llvm/IR/RuntimeLibcalls.def index 903db6c7049877..fd5c0fc7aba0a6 100644 --- a/llvm/include/llvm/IR/RuntimeLibcalls.def +++ b/llvm/include/llvm/IR/RuntimeLibcalls.def @@ -85,6 +85,11 @@ HANDLE_LIBCALL(NEG_I64, "__negdi2") HANDLE_LIBCALL(CTLZ_I32, "__clzsi2") HANDLE_LIBCALL(CTLZ_I64, "__clzdi2") HANDLE_LIBCALL(CTLZ_I128, "__clzti2") +HANDLE_LIBCALL(POPCNT_I8, nullptr) +HANDLE_LIBCALL(POPCNT_I16, nullptr) +HANDLE_LIBCALL(POPCNT_I24, nullptr) +HANDLE_LIBCALL(POPCNT_I32, "__popcountsi2") +HANDLE_LIBCALL(POPCNT_I64, "__popcountdi2") // Floating-point HANDLE_LIBCALL(ADD_F32, "__addsf3") diff --git a/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp b/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp index 1d7be54de3b045..b23dffe6df3a55 100644 --- a/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp +++ b/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp @@ -93,6 +93,7 @@ static bool isArtifact(const MachineInstr &MI) { switch (MI.getOpcode()) { default: return false; + case TargetOpcode::G_IMPLICIT_DEF: case TargetOpcode::G_TRUNC: case TargetOpcode::G_ZEXT: case TargetOpcode::G_ANYEXT: @@ -102,9 +103,13 @@ static bool isArtifact(const MachineInstr &MI) { case TargetOpcode::G_CONCAT_VECTORS: case TargetOpcode::G_BUILD_VECTOR: case TargetOpcode::G_EXTRACT: + case TargetOpcode::G_INSERT: return true; } } +static bool isPreISelGenericOpcodeOrCopy(unsigned Opcode) { + return isPreISelGenericOpcode(Opcode) || Opcode == TargetOpcode::COPY; +} using InstListTy = GISelWorkList<256>; using ArtifactListTy = GISelWorkList<128>; @@ -112,19 +117,22 @@ namespace { class LegalizerWorkListManager : public GISelChangeObserver { InstListTy &InstList; ArtifactListTy &ArtifactList; + MachineRegisterInfo &MRI; #ifndef NDEBUG SmallVector NewMIs; #endif public: - LegalizerWorkListManager(InstListTy &Insts, ArtifactListTy &Arts) - : InstList(Insts), ArtifactList(Arts) {} + LegalizerWorkListManager(InstListTy &Insts, ArtifactListTy &Arts, + MachineRegisterInfo &MRI) + : InstList(Insts), ArtifactList(Arts), MRI(MRI) {} void createdOrChangedInstr(MachineInstr &MI) { // Only legalize pre-isel generic instructions. // Legalization process could generate Target specific pseudo // instructions with generic types. Don't record them - if (isPreISelGenericOpcode(MI.getOpcode())) { + // Do record copies in case they become dead and use an artifact. + if (isPreISelGenericOpcodeOrCopy(MI.getOpcode())) { if (isArtifact(MI)) ArtifactList.insert(&MI); else @@ -132,6 +140,13 @@ class LegalizerWorkListManager : public GISelChangeObserver { } } + void maybeRemovingUses(MachineInstr &MI) { + for (const MachineOperand &MO : MI.explicit_uses()) + if (MO.isReg() && MO.getReg().isVirtual()) + for (MachineInstr &DefMI : MRI.def_instructions(MO.getReg())) + createdOrChangedInstr(DefMI); + } + void createdInstr(MachineInstr &MI) override { LLVM_DEBUG(NewMIs.push_back(&MI)); createdOrChangedInstr(MI); @@ -147,12 +162,14 @@ class LegalizerWorkListManager : public GISelChangeObserver { void erasingInstr(MachineInstr &MI) override { LLVM_DEBUG(dbgs() << ".. .. Erasing: " << MI); + maybeRemovingUses(MI); InstList.remove(&MI); ArtifactList.remove(&MI); } void changingInstr(MachineInstr &MI) override { LLVM_DEBUG(dbgs() << ".. .. Changing MI: " << MI); + maybeRemovingUses(MI); } void changedInstr(MachineInstr &MI) override { @@ -185,7 +202,7 @@ Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI, for (MachineInstr &MI : *MBB) { // Only legalize pre-isel generic instructions: others don't have types // and are assumed to be legal. - if (!isPreISelGenericOpcode(MI.getOpcode())) + if (!isPreISelGenericOpcodeOrCopy(MI.getOpcode())) continue; if (isArtifact(MI)) ArtifactList.deferred_insert(&MI); @@ -197,7 +214,7 @@ Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI, InstList.finalize(); // This observer keeps the worklists updated. - LegalizerWorkListManager WorkListObserver(InstList, ArtifactList); + LegalizerWorkListManager WorkListObserver(InstList, ArtifactList, MRI); // We want both WorkListObserver as well as all the auxiliary observers (e.g. // CSEInfo) to observe all changes. Use the wrapper observer. GISelObserverWrapper WrapperObserver(&WorkListObserver); @@ -220,8 +237,9 @@ Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI, unsigned NumArtifacts = ArtifactList.size(); while (!InstList.empty()) { MachineInstr &MI = *InstList.pop_back_val(); - assert(isPreISelGenericOpcode(MI.getOpcode()) && - "Expecting generic opcode"); + assert(MI.getParent() && "Instruction deleted?"); + assert(isPreISelGenericOpcodeOrCopy(MI.getOpcode()) && + "Expecting generic opcode or copy"); if (isTriviallyDead(MI, MRI)) { LLVM_DEBUG(dbgs() << MI << "Is dead; erasing.\n"); MI.eraseFromParentAndMarkDBGValuesForRemoval(); @@ -269,8 +287,8 @@ Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI, LocObserver.checkpoint(); while (!ArtifactList.empty()) { MachineInstr &MI = *ArtifactList.pop_back_val(); - assert(isPreISelGenericOpcode(MI.getOpcode()) && - "Expecting generic opcode"); + assert(MI.getParent() && "Instruction deleted?"); + assert(isArtifact(MI) && "Expecting artifact"); if (isTriviallyDead(MI, MRI)) { LLVM_DEBUG(dbgs() << MI << "Is dead\n"); RemoveDeadInstFromLists(&MI); diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp index 38590656d1f4b4..5f48ec7c33185d 100644 --- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp +++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp @@ -35,32 +35,31 @@ using namespace LegalizeActions; /// Returns the number of \p NarrowTy elements needed to reconstruct \p OrigTy, /// with any leftover piece as type \p LeftoverTy /// -/// Returns -1 in the first element of the pair if the breakdown is not -/// satisfiable. -static std::pair -getNarrowTypeBreakDown(LLT OrigTy, LLT NarrowTy, LLT &LeftoverTy) { - assert(!LeftoverTy.isValid() && "this is an out argument"); - +/// Returns 0 if the breakdown is not satisfiable. +static unsigned getNarrowTypeBreakDown(LLT OrigTy, LLT NarrowTy, + LLT &LeftoverTy) { unsigned Size = OrigTy.getSizeInBits(); unsigned NarrowSize = NarrowTy.getSizeInBits(); unsigned NumParts = Size / NarrowSize; unsigned LeftoverSize = Size - NumParts * NarrowSize; + assert( + (!LeftoverTy.isValid() || LeftoverTy.getSizeInBits() == LeftoverSize) && + "LeftoverTy already set and doesn't match this breakdown"); assert(Size > NarrowSize); if (LeftoverSize == 0) - return {NumParts, 0}; + return NumParts; if (NarrowTy.isVector()) { unsigned EltSize = OrigTy.getScalarSizeInBits(); if (LeftoverSize % EltSize != 0) - return {-1, -1}; + return 0; LeftoverTy = LLT::scalarOrVector(LeftoverSize / EltSize, EltSize); } else { LeftoverTy = LLT::scalar(LeftoverSize); } - int NumLeftover = LeftoverSize / LeftoverTy.getSizeInBits(); - return std::make_pair(NumParts, NumLeftover); + return NumParts; } static Type *getFloatTypeForLLT(LLVMContext &Ctx, LLT Ty) { @@ -102,6 +101,8 @@ LegalizerHelper::legalizeInstrStep(MachineInstr &MI) { MIRBuilder.setInstrAndDebugLoc(MI); + if (MI.getOpcode() == TargetOpcode::COPY) + return AlreadyLegal; if (MI.getOpcode() == TargetOpcode::G_INTRINSIC || MI.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS) return LI.legalizeIntrinsic(*this, MI) ? Legalized : UnableToLegalize; @@ -124,7 +125,7 @@ LegalizerHelper::legalizeInstrStep(MachineInstr &MI) { return bitcast(MI, Step.TypeIdx, Step.NewType); case Lower: LLVM_DEBUG(dbgs() << ".. Lower\n"); - return lower(MI, Step.TypeIdx, Step.NewType); + return lower(MI, Step.TypeIdx); case FewerElements: LLVM_DEBUG(dbgs() << ".. Reduce number of elements\n"); return fewerElementsVector(MI, Step.TypeIdx, Step.NewType); @@ -133,7 +134,7 @@ LegalizerHelper::legalizeInstrStep(MachineInstr &MI) { return moreElementsVector(MI, Step.TypeIdx, Step.NewType); case Custom: LLVM_DEBUG(dbgs() << ".. Custom legalization\n"); - return LI.legalizeCustom(*this, MI) ? Legalized : UnableToLegalize; + return LI.legalizeCustom(MI, MRI, MIRBuilder, Observer, *this); default: LLVM_DEBUG(dbgs() << ".. Unable to legalize\n"); return UnableToLegalize; @@ -142,63 +143,56 @@ LegalizerHelper::legalizeInstrStep(MachineInstr &MI) { void LegalizerHelper::extractParts(Register Reg, LLT Ty, int NumParts, SmallVectorImpl &VRegs) { + assert(NumParts && "Expected at least one part"); + if (NumParts == 1) { + VRegs.push_back(Reg); + return; + } for (int i = 0; i < NumParts; ++i) VRegs.push_back(MRI.createGenericVirtualRegister(Ty)); MIRBuilder.buildUnmerge(VRegs, Reg); } -bool LegalizerHelper::extractParts(Register Reg, LLT RegTy, - LLT MainTy, LLT &LeftoverTy, +bool LegalizerHelper::extractParts(Register Reg, LLT RegTy, LLT MainTy, SmallVectorImpl &VRegs, - SmallVectorImpl &LeftoverRegs) { - assert(!LeftoverTy.isValid() && "this is an out argument"); + LLT &LeftoverTy, Register &LeftoverReg) { + assert(!LeftoverReg.isValid() && "this is an out argument"); - unsigned RegSize = RegTy.getSizeInBits(); - unsigned MainSize = MainTy.getSizeInBits(); - unsigned NumParts = RegSize / MainSize; - unsigned LeftoverSize = RegSize - NumParts * MainSize; + unsigned NumParts = getNarrowTypeBreakDown(RegTy, MainTy, LeftoverTy); + if (!NumParts) + return false; // Use an unmerge when possible. - if (LeftoverSize == 0) { + if (!LeftoverTy.isValid()) { for (unsigned I = 0; I < NumParts; ++I) VRegs.push_back(MRI.createGenericVirtualRegister(MainTy)); MIRBuilder.buildUnmerge(VRegs, Reg); return true; } - if (MainTy.isVector()) { - unsigned EltSize = MainTy.getScalarSizeInBits(); - if (LeftoverSize % EltSize != 0) - return false; - LeftoverTy = LLT::scalarOrVector(LeftoverSize / EltSize, EltSize); - } else { - LeftoverTy = LLT::scalar(LeftoverSize); - } - // For irregular sizes, extract the individual parts. - for (unsigned I = 0; I != NumParts; ++I) { - Register NewReg = MRI.createGenericVirtualRegister(MainTy); - VRegs.push_back(NewReg); - MIRBuilder.buildExtract(NewReg, Reg, MainSize * I); - } + unsigned MainSize = MainTy.getSizeInBits(); + unsigned Offset = 0; + for (unsigned I = 0; I != NumParts; ++I, Offset += MainSize) + VRegs.push_back(MIRBuilder.buildExtract(MainTy, Reg, Offset).getReg(0)); - for (unsigned Offset = MainSize * NumParts; Offset < RegSize; - Offset += LeftoverSize) { - Register NewReg = MRI.createGenericVirtualRegister(LeftoverTy); - LeftoverRegs.push_back(NewReg); - MIRBuilder.buildExtract(NewReg, Reg, Offset); - } + if (LeftoverTy.isValid()) + LeftoverReg = MIRBuilder.buildExtract(LeftoverTy, Reg, Offset).getReg(0); return true; } -void LegalizerHelper::insertParts(Register DstReg, - LLT ResultTy, LLT PartTy, - ArrayRef PartRegs, - LLT LeftoverTy, - ArrayRef LeftoverRegs) { +void LegalizerHelper::insertParts(Register DstReg, LLT ResultTy, LLT PartTy, + ArrayRef PartRegs, LLT LeftoverTy, + Register LeftoverReg) { if (!LeftoverTy.isValid()) { - assert(LeftoverRegs.empty()); + assert(!LeftoverReg.isValid()); + + if (ResultTy == PartTy) { + assert(PartRegs.size() == 1); + MIRBuilder.buildCopy(DstReg, PartRegs[0]); + return; + } if (!ResultTy.isVector()) { MIRBuilder.buildMerge(DstReg, PartRegs); @@ -213,7 +207,6 @@ void LegalizerHelper::insertParts(Register DstReg, } unsigned PartSize = PartTy.getSizeInBits(); - unsigned LeftoverPartSize = LeftoverTy.getSizeInBits(); Register CurResultReg = MRI.createGenericVirtualRegister(ResultTy); MIRBuilder.buildUndef(CurResultReg); @@ -226,15 +219,8 @@ void LegalizerHelper::insertParts(Register DstReg, Offset += PartSize; } - for (unsigned I = 0, E = LeftoverRegs.size(); I != E; ++I) { - // Use the original output register for the final insert to avoid a copy. - Register NewResultReg = (I + 1 == E) ? - DstReg : MRI.createGenericVirtualRegister(ResultTy); - - MIRBuilder.buildInsert(NewResultReg, CurResultReg, LeftoverRegs[I], Offset); - CurResultReg = NewResultReg; - Offset += LeftoverPartSize; - } + if (LeftoverTy.isValid()) + MIRBuilder.buildInsert(DstReg, CurResultReg, LeftoverReg, Offset); } /// Return the result registers of G_UNMERGE_VALUES \p MI in \p Regs @@ -637,6 +623,9 @@ LegalizerHelper::libcall(MachineInstr &MI) { switch (MI.getOpcode()) { default: return UnableToLegalize; + case TargetOpcode::G_AND: + case TargetOpcode::G_OR: + case TargetOpcode::G_XOR: case TargetOpcode::G_SDIV: case TargetOpcode::G_UDIV: case TargetOpcode::G_SREM: @@ -648,6 +637,27 @@ LegalizerHelper::libcall(MachineInstr &MI) { return Status; break; } + case TargetOpcode::G_CTPOP: { + Type *ResTy = IntegerType::get(Ctx, Size); + unsigned OpSize = MRI.getType(MI.getOperand(1).getReg()).getSizeInBits(); + Type *OpTy = IntegerType::get(Ctx, OpSize); + RTLIB::Libcall Libcall = getRTLibDesc(MI.getOpcode(), OpSize); + createLibcall(MIRBuilder, Libcall, {MI.getOperand(0).getReg(), ResTy}, + {{MI.getOperand(1).getReg(), OpTy}}); + break; + } + case TargetOpcode::G_SHL: + case TargetOpcode::G_LSHR: + case TargetOpcode::G_ASHR: { + Type *OpTy = IntegerType::get(Ctx, Size); + RTLIB::Libcall Libcall = getRTLibDesc(MI.getOpcode(), Size); + Register AmountReg = MI.getOperand(2).getReg(); + Type *AmountTy = + IntegerType::get(Ctx, MRI.getType(AmountReg).getSizeInBits()); + createLibcall(MIRBuilder, Libcall, {MI.getOperand(0).getReg(), OpTy}, + {{MI.getOperand(1).getReg(), OpTy}, {AmountReg, AmountTy}}); + break; + } case TargetOpcode::G_FADD: case TargetOpcode::G_FSUB: case TargetOpcode::G_FMUL: @@ -729,23 +739,25 @@ LegalizerHelper::libcall(MachineInstr &MI) { LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, unsigned TypeIdx, LLT NarrowTy) { - uint64_t SizeOp0 = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits(); - uint64_t NarrowSize = NarrowTy.getSizeInBits(); + Register DstReg = MI.getOperand(0).getReg(); + LLT DstTy = MRI.getType(DstReg); + unsigned DstSize = DstTy.getSizeInBits(); + unsigned NarrowSize = NarrowTy.getSizeInBits(); - switch (MI.getOpcode()) { + switch (unsigned Opc = MI.getOpcode()) { default: return UnableToLegalize; case TargetOpcode::G_IMPLICIT_DEF: { Register DstReg = MI.getOperand(0).getReg(); LLT DstTy = MRI.getType(DstReg); - // If SizeOp0 is not an exact multiple of NarrowSize, emit + // If DstSize is not an exact multiple of NarrowSize, emit // G_ANYEXT(G_IMPLICIT_DEF). Cast result to vector if needed. // FIXME: Although this would also be legal for the general case, it causes // a lot of regressions in the emitted code (superfluous COPYs, artifact // combines not being hit). This seems to be a problem related to the // artifact combiner. - if (SizeOp0 % NarrowSize != 0) { + if (DstSize % NarrowSize != 0) { LLT ImplicitTy = NarrowTy; if (DstTy.isVector()) ImplicitTy = LLT::vector(DstTy.getNumElements(), ImplicitTy); @@ -757,7 +769,7 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, return Legalized; } - int NumParts = SizeOp0 / NarrowSize; + int NumParts = DstSize / NarrowSize; SmallVector DstRegs; for (int i = 0; i < NumParts; ++i) @@ -771,11 +783,8 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, return Legalized; } case TargetOpcode::G_CONSTANT: { - LLT Ty = MRI.getType(MI.getOperand(0).getReg()); const APInt &Val = MI.getOperand(1).getCImm()->getValue(); - unsigned TotalSize = Ty.getSizeInBits(); - unsigned NarrowSize = NarrowTy.getSizeInBits(); - int NumParts = TotalSize / NarrowSize; + int NumParts = DstSize / NarrowSize; SmallVector PartRegs; for (int I = 0; I != NumParts; ++I) { @@ -786,18 +795,15 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, } LLT LeftoverTy; - unsigned LeftoverBits = TotalSize - NumParts * NarrowSize; - SmallVector LeftoverRegs; - if (LeftoverBits != 0) { + Register LeftoverReg; + if (unsigned LeftoverBits = DstSize - NumParts * NarrowSize) { LeftoverTy = LLT::scalar(LeftoverBits); - auto K = MIRBuilder.buildConstant( - LeftoverTy, - Val.lshr(NumParts * NarrowSize).trunc(LeftoverBits)); - LeftoverRegs.push_back(K.getReg(0)); + auto C = MIRBuilder.buildConstant( + LeftoverTy, Val.lshr(NumParts * NarrowSize).trunc(LeftoverBits)); + LeftoverReg = C.getReg(0); } - insertParts(MI.getOperand(0).getReg(), - Ty, NarrowTy, PartRegs, LeftoverTy, LeftoverRegs); + insertParts(DstReg, DstTy, NarrowTy, PartRegs, LeftoverTy, LeftoverReg); MI.eraseFromParent(); return Legalized; @@ -810,14 +816,30 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, if (TypeIdx != 1) return UnableToLegalize; - uint64_t SizeOp1 = MRI.getType(MI.getOperand(1).getReg()).getSizeInBits(); - if (NarrowTy.getSizeInBits() * 2 != SizeOp1) { - LLVM_DEBUG(dbgs() << "Can't narrow trunc to type " << NarrowTy << "\n"); + Register SrcReg = MI.getOperand(1).getReg(); + LLT SrcTy = MRI.getType(SrcReg); + assert(SrcTy.isScalar() && "Expected scalar"); + + SmallVector PartRegs; + LLT LeftoverTy; + Register LeftoverReg; + if (!extractParts(SrcReg, SrcTy, NarrowTy, PartRegs, LeftoverTy, + LeftoverReg)) return UnableToLegalize; + + unsigned NumParts = DstSize / NarrowSize; + auto TruncRegs = makeArrayRef(PartRegs).take_front(NumParts); + if (DstSize > NumParts * NarrowSize) { + LeftoverTy = LLT::scalar(DstSize - NumParts * NarrowSize); + if (NumParts < PartRegs.size()) + LeftoverReg = PartRegs[NumParts]; + LeftoverReg = MIRBuilder.buildTrunc(LeftoverTy, LeftoverReg).getReg(0); + } else { + LeftoverTy = LLT(); + LeftoverReg = Register(); } - auto Unmerge = MIRBuilder.buildUnmerge(NarrowTy, MI.getOperand(1)); - MIRBuilder.buildCopy(MI.getOperand(0), Unmerge.getReg(0)); + insertParts(DstReg, DstTy, NarrowTy, TruncRegs, LeftoverTy, LeftoverReg); MI.eraseFromParent(); return Legalized; } @@ -825,73 +847,13 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, case TargetOpcode::G_FREEZE: return reduceOperationWidth(MI, TypeIdx, NarrowTy); - case TargetOpcode::G_ADD: { - // FIXME: add support for when SizeOp0 isn't an exact multiple of - // NarrowSize. - if (SizeOp0 % NarrowSize != 0) - return UnableToLegalize; - // Expand in terms of carry-setting/consuming G_ADDE instructions. - int NumParts = SizeOp0 / NarrowTy.getSizeInBits(); - - SmallVector Src1Regs, Src2Regs, DstRegs; - extractParts(MI.getOperand(1).getReg(), NarrowTy, NumParts, Src1Regs); - extractParts(MI.getOperand(2).getReg(), NarrowTy, NumParts, Src2Regs); - - Register CarryIn; - for (int i = 0; i < NumParts; ++i) { - Register DstReg = MRI.createGenericVirtualRegister(NarrowTy); - Register CarryOut = MRI.createGenericVirtualRegister(LLT::scalar(1)); - - if (i == 0) - MIRBuilder.buildUAddo(DstReg, CarryOut, Src1Regs[i], Src2Regs[i]); - else { - MIRBuilder.buildUAdde(DstReg, CarryOut, Src1Regs[i], - Src2Regs[i], CarryIn); - } - - DstRegs.push_back(DstReg); - CarryIn = CarryOut; - } - Register DstReg = MI.getOperand(0).getReg(); - if(MRI.getType(DstReg).isVector()) - MIRBuilder.buildBuildVector(DstReg, DstRegs); - else - MIRBuilder.buildMerge(DstReg, DstRegs); - MI.eraseFromParent(); - return Legalized; - } - case TargetOpcode::G_SUB: { - // FIXME: add support for when SizeOp0 isn't an exact multiple of - // NarrowSize. - if (SizeOp0 % NarrowSize != 0) - return UnableToLegalize; - - int NumParts = SizeOp0 / NarrowTy.getSizeInBits(); - - SmallVector Src1Regs, Src2Regs, DstRegs; - extractParts(MI.getOperand(1).getReg(), NarrowTy, NumParts, Src1Regs); - extractParts(MI.getOperand(2).getReg(), NarrowTy, NumParts, Src2Regs); - - Register DstReg = MRI.createGenericVirtualRegister(NarrowTy); - Register BorrowOut = MRI.createGenericVirtualRegister(LLT::scalar(1)); - MIRBuilder.buildInstr(TargetOpcode::G_USUBO, {DstReg, BorrowOut}, - {Src1Regs[0], Src2Regs[0]}); - DstRegs.push_back(DstReg); - Register BorrowIn = BorrowOut; - for (int i = 1; i < NumParts; ++i) { - DstReg = MRI.createGenericVirtualRegister(NarrowTy); - BorrowOut = MRI.createGenericVirtualRegister(LLT::scalar(1)); - - MIRBuilder.buildInstr(TargetOpcode::G_USUBE, {DstReg, BorrowOut}, - {Src1Regs[i], Src2Regs[i], BorrowIn}); - - DstRegs.push_back(DstReg); - BorrowIn = BorrowOut; - } - MIRBuilder.buildMerge(MI.getOperand(0), DstRegs); - MI.eraseFromParent(); - return Legalized; - } + case TargetOpcode::G_ADD: + case TargetOpcode::G_UADDO: + case TargetOpcode::G_UADDE: + case TargetOpcode::G_SUB: + case TargetOpcode::G_USUBO: + case TargetOpcode::G_USUBE: + return narrowScalarExtended(MI, TypeIdx, NarrowTy); case TargetOpcode::G_MUL: case TargetOpcode::G_UMULH: return narrowScalarMul(MI, NarrowTy); @@ -919,19 +881,17 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, } case TargetOpcode::G_ZEXTLOAD: case TargetOpcode::G_SEXTLOAD: { - bool ZExt = MI.getOpcode() == TargetOpcode::G_ZEXTLOAD; Register DstReg = MI.getOperand(0).getReg(); Register PtrReg = MI.getOperand(1).getReg(); Register TmpReg = MRI.createGenericVirtualRegister(NarrowTy); auto &MMO = **MI.memoperands_begin(); - if (MMO.getSizeInBits() == NarrowSize) { + if (MMO.getSizeInBits() == NarrowSize) MIRBuilder.buildLoad(TmpReg, PtrReg, MMO); - } else { - MIRBuilder.buildLoadInstr(MI.getOpcode(), TmpReg, PtrReg, MMO); - } + else + MIRBuilder.buildLoadInstr(Opc, TmpReg, PtrReg, MMO); - if (ZExt) + if (Opc == TargetOpcode::G_ZEXTLOAD) MIRBuilder.buildZExt(DstReg, TmpReg); else MIRBuilder.buildSExt(DstReg, TmpReg); @@ -947,7 +907,7 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, if (SrcTy.isVector()) return UnableToLegalize; - int NumParts = SizeOp0 / NarrowSize; + int NumParts = DstSize / NarrowSize; unsigned HandledSize = NumParts * NarrowTy.getSizeInBits(); unsigned LeftoverBits = SrcTy.getSizeInBits() - HandledSize; if (SrcTy.isVector() && LeftoverBits != 0) @@ -1024,28 +984,35 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, Observer.changedInstr(MI); return Legalized; case TargetOpcode::G_PHI: { - unsigned NumParts = SizeOp0 / NarrowSize; - SmallVector DstRegs(NumParts); - SmallVector, 2> SrcRegs(MI.getNumOperands() / 2); - Observer.changingInstr(MI); + unsigned NumPredecessors = MI.getNumOperands() / 2; + LLT LeftoverTy; + SmallVector, 2> SrcRegs(NumPredecessors); + SmallVector LeftoverSrcRegs(NumPredecessors); + SmallVector DstRegs; + Register LeftoverDstReg; for (unsigned i = 1; i < MI.getNumOperands(); i += 2) { MachineBasicBlock &OpMBB = *MI.getOperand(i + 1).getMBB(); MIRBuilder.setInsertPt(OpMBB, OpMBB.getFirstTerminator()); - extractParts(MI.getOperand(i).getReg(), NarrowTy, NumParts, - SrcRegs[i / 2]); + extractParts(MI.getOperand(i).getReg(), DstTy, NarrowTy, SrcRegs[i / 2], + LeftoverTy, LeftoverSrcRegs[i / 2]); } MachineBasicBlock &MBB = *MI.getParent(); MIRBuilder.setInsertPt(MBB, MI); - for (unsigned i = 0; i < NumParts; ++i) { - DstRegs[i] = MRI.createGenericVirtualRegister(NarrowTy); - MachineInstrBuilder MIB = - MIRBuilder.buildInstr(TargetOpcode::G_PHI).addDef(DstRegs[i]); + DstRegs.resize(SrcRegs[0].size()); + for (unsigned i = 0; i < DstRegs.size(); ++i) { + auto MIB = MIRBuilder.buildInstr(TargetOpcode::G_PHI, {NarrowTy}, {}); + DstRegs[i] = MIB.getReg(0); for (unsigned j = 1; j < MI.getNumOperands(); j += 2) MIB.addUse(SrcRegs[j / 2][i]).add(MI.getOperand(j + 1)); } + if (LeftoverTy.isValid()) { + auto MIB = MIRBuilder.buildInstr(TargetOpcode::G_PHI, {LeftoverTy}, {}); + LeftoverDstReg = MIB.getReg(0); + for (unsigned j = 1; j < MI.getNumOperands(); j += 2) + MIB.addUse(LeftoverSrcRegs[j / 2]).add(MI.getOperand(j + 1)); + } MIRBuilder.setInsertPt(MBB, MBB.getFirstNonPHI()); - MIRBuilder.buildMerge(MI.getOperand(0), DstRegs); - Observer.changedInstr(MI); + insertParts(DstReg, DstTy, NarrowTy, DstRegs, LeftoverTy, LeftoverDstReg); MI.eraseFromParent(); return Legalized; } @@ -1054,7 +1021,7 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, if (TypeIdx != 2) return UnableToLegalize; - int OpIdx = MI.getOpcode() == TargetOpcode::G_EXTRACT_VECTOR_ELT ? 2 : 3; + int OpIdx = Opc == TargetOpcode::G_EXTRACT_VECTOR_ELT ? 2 : 3; Observer.changingInstr(MI); narrowScalarSrc(MI, NarrowTy, OpIdx); Observer.changedInstr(MI); @@ -1125,9 +1092,9 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, // component containing the extension point becomes a narrower SEXT_INREG. // Components above it are ashr'd from the component containing the // extension point. - if (SizeOp0 % NarrowSize != 0) + if (DstSize % NarrowSize != 0) return UnableToLegalize; - int NumParts = SizeOp0 / NarrowSize; + int NumParts = DstSize / NarrowSize; // List the registers where the destination will be scattered. SmallVector DstRegs; @@ -1184,12 +1151,12 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, } case TargetOpcode::G_BSWAP: case TargetOpcode::G_BITREVERSE: { - if (SizeOp0 % NarrowSize != 0) + if (DstSize % NarrowSize != 0) return UnableToLegalize; Observer.changingInstr(MI); SmallVector SrcRegs, DstRegs; - unsigned NumParts = SizeOp0 / NarrowSize; + unsigned NumParts = DstSize / NarrowSize; extractParts(MI.getOperand(1).getReg(), NarrowTy, NumParts, SrcRegs); for (unsigned i = 0; i < NumParts; ++i) { @@ -2283,11 +2250,13 @@ LegalizerHelper::bitcast(MachineInstr &MI, unsigned TypeIdx, LLT CastTy) { } } -LegalizerHelper::LegalizeResult -LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT Ty) { +LegalizerHelper::LegalizeResult LegalizerHelper::lower(MachineInstr &MI, + unsigned TypeIdx) { using namespace TargetOpcode; - switch(MI.getOpcode()) { + LLT Ty = MRI.getType(MI.getOperand(0).getReg()); + + switch (MI.getOpcode()) { default: return UnableToLegalize; case TargetOpcode::G_BITCAST: @@ -2341,6 +2310,32 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT Ty) { } return Legalized; } + case TargetOpcode::G_SMULH: + case TargetOpcode::G_UMULH: { + Register ResReg = MI.getOperand(0).getReg(); + Register LHSReg = MI.getOperand(1).getReg(); + Register RHSReg = MI.getOperand(2).getReg(); + LLT FullTy = LLT::scalar(Ty.getSizeInBits() * 2); + + unsigned ExtOpc = MI.getOpcode() == TargetOpcode::G_SMULH + ? TargetOpcode::G_SEXT + : TargetOpcode::G_ZEXT; + auto ExtLHSI = MIRBuilder.buildInstr(ExtOpc, {FullTy}, {LHSReg}); + auto ExtRHSI = MIRBuilder.buildInstr(ExtOpc, {FullTy}, {RHSReg}); + auto MulI = MIRBuilder.buildMul(FullTy, ExtLHSI, ExtRHSI); + auto ShiftAmtI = MIRBuilder.buildConstant(FullTy, Ty.getSizeInBits()); + auto ShiftI = MIRBuilder.buildLShr(FullTy, MulI, ShiftAmtI); + MIRBuilder.buildTrunc(ResReg, ShiftI); + + MI.eraseFromParent(); + return Legalized; + } + case TargetOpcode::G_UADDSAT: + case TargetOpcode::G_USUBSAT: + return lowerUADDSAT_USUBSAT(MI); + case TargetOpcode::G_SADDSAT: + case TargetOpcode::G_SSUBSAT: + return lowerSADDSAT_SSUBSAT(MI); case TargetOpcode::G_FNEG: { // TODO: Handle vector types once we are able to // represent them. @@ -2708,25 +2703,26 @@ LegalizerHelper::fewerElementsVectorMultiEltType( // All of the operands need to have the same number of elements, so if we can // determine a type breakdown for the result type, we can for all of the // source types. - int NumParts = getNarrowTypeBreakDown(DstTy, NarrowTy0, LeftoverTy0).first; - if (NumParts < 0) + unsigned NumParts = getNarrowTypeBreakDown(DstTy, NarrowTy0, LeftoverTy0); + if (!NumParts) return UnableToLegalize; SmallVector NewInsts; - SmallVector DstRegs, LeftoverDstRegs; - SmallVector PartRegs, LeftoverRegs; + SmallVector DstRegs, PartRegs; + Register LeftoverDstReg; for (unsigned I = 1, E = MI.getNumOperands(); I != E; ++I) { Register SrcReg = MI.getOperand(I).getReg(); LLT SrcTyI = MRI.getType(SrcReg); LLT NarrowTyI = LLT::scalarOrVector(NewNumElts, SrcTyI.getScalarType()); LLT LeftoverTyI; + Register LeftoverReg; // Split this operand into the requested typed registers, and any leftover // required to reproduce the original type. - if (!extractParts(SrcReg, SrcTyI, NarrowTyI, LeftoverTyI, PartRegs, - LeftoverRegs)) + if (!extractParts(SrcReg, SrcTyI, NarrowTyI, PartRegs, LeftoverTyI, + LeftoverReg)) return UnableToLegalize; if (I == 1) { @@ -2740,15 +2736,14 @@ LegalizerHelper::fewerElementsVectorMultiEltType( DstRegs.push_back(PartDstReg); } - for (Register LeftoverReg : LeftoverRegs) { - Register PartDstReg = MRI.createGenericVirtualRegister(LeftoverTy0); + if (LeftoverReg.isValid()) { + LeftoverDstReg = MRI.createGenericVirtualRegister(LeftoverTy0); NewInsts.push_back(MIRBuilder.buildInstrNoInsert(MI.getOpcode()) - .addDef(PartDstReg) + .addDef(LeftoverDstReg) .addUse(LeftoverReg)); - LeftoverDstRegs.push_back(PartDstReg); } } else { - assert(NewInsts.size() == PartRegs.size() + LeftoverRegs.size()); + assert(NewInsts.size() == PartRegs.size() + LeftoverReg.isValid()); // Add the newly created operand splits to the existing instructions. The // odd-sized pieces are ordered after the requested NarrowTyArg sized @@ -2756,19 +2751,18 @@ LegalizerHelper::fewerElementsVectorMultiEltType( unsigned InstCount = 0; for (unsigned J = 0, JE = PartRegs.size(); J != JE; ++J) NewInsts[InstCount++].addUse(PartRegs[J]); - for (unsigned J = 0, JE = LeftoverRegs.size(); J != JE; ++J) - NewInsts[InstCount++].addUse(LeftoverRegs[J]); + if (LeftoverReg.isValid()) + NewInsts[InstCount++].addUse(LeftoverReg); } PartRegs.clear(); - LeftoverRegs.clear(); } // Insert the newly built operations and rebuild the result register. for (auto &MIB : NewInsts) MIRBuilder.insertInstr(MIB); - insertParts(DstReg, DstTy, NarrowTy0, DstRegs, LeftoverTy0, LeftoverDstRegs); + insertParts(DstReg, DstTy, NarrowTy0, DstRegs, LeftoverTy0, LeftoverDstReg); MI.eraseFromParent(); return Legalized; @@ -2971,19 +2965,17 @@ LegalizerHelper::fewerElementsVectorPhi(MachineInstr &MI, unsigned TypeIdx, // All of the operands need to have the same number of elements, so if we can // determine a type breakdown for the result type, we can for all of the // source types. - int NumParts, NumLeftover; - std::tie(NumParts, NumLeftover) - = getNarrowTypeBreakDown(PhiTy, NarrowTy, LeftoverTy); - if (NumParts < 0) + unsigned NumParts = getNarrowTypeBreakDown(PhiTy, NarrowTy, LeftoverTy); + if (!NumParts) return UnableToLegalize; - SmallVector DstRegs, LeftoverDstRegs; + SmallVector DstRegs; + Register LeftoverDstReg; SmallVector NewInsts; - const int TotalNumParts = NumParts + NumLeftover; - // Insert the new phis in the result block first. - for (int I = 0; I != TotalNumParts; ++I) { + unsigned TotalNumParts = NumParts + LeftoverTy.isValid(); + for (unsigned I = 0; I != TotalNumParts; ++I) { LLT Ty = I < NumParts ? NarrowTy : LeftoverTy; Register PartDstReg = MRI.createGenericVirtualRegister(Ty); NewInsts.push_back(MIRBuilder.buildInstr(TargetOpcode::G_PHI) @@ -2991,35 +2983,33 @@ LegalizerHelper::fewerElementsVectorPhi(MachineInstr &MI, unsigned TypeIdx, if (I < NumParts) DstRegs.push_back(PartDstReg); else - LeftoverDstRegs.push_back(PartDstReg); + LeftoverDstReg = PartDstReg; } MachineBasicBlock *MBB = MI.getParent(); MIRBuilder.setInsertPt(*MBB, MBB->getFirstNonPHI()); - insertParts(DstReg, PhiTy, NarrowTy, DstRegs, LeftoverTy, LeftoverDstRegs); + insertParts(DstReg, PhiTy, NarrowTy, DstRegs, LeftoverTy, LeftoverDstReg); - SmallVector PartRegs, LeftoverRegs; + SmallVector PartRegs; + Register LeftoverReg; // Insert code to extract the incoming values in each predecessor block. for (unsigned I = 1, E = MI.getNumOperands(); I != E; I += 2) { PartRegs.clear(); - LeftoverRegs.clear(); Register SrcReg = MI.getOperand(I).getReg(); MachineBasicBlock &OpMBB = *MI.getOperand(I + 1).getMBB(); MIRBuilder.setInsertPt(OpMBB, OpMBB.getFirstTerminator()); - LLT Unused; - if (!extractParts(SrcReg, PhiTy, NarrowTy, Unused, PartRegs, - LeftoverRegs)) + if (!extractParts(SrcReg, PhiTy, NarrowTy, PartRegs, LeftoverTy, LeftoverReg)) return UnableToLegalize; // Add the newly created operand splits to the existing instructions. The // odd-sized pieces are ordered after the requested NarrowTyArg sized // pieces. - for (int J = 0; J != TotalNumParts; ++J) { + for (unsigned J = 0; J != TotalNumParts; ++J) { MachineInstrBuilder MIB = NewInsts[J]; - MIB.addUse(J < NumParts ? PartRegs[J] : LeftoverRegs[J - NumParts]); + MIB.addUse(J < NumParts ? PartRegs[J] : LeftoverReg); MIB.addMBB(&OpMBB); } } @@ -3153,21 +3143,17 @@ LegalizerHelper::reduceLoadStoreWidth(MachineInstr &MI, unsigned TypeIdx, return UnableToLegalize; } - int NumParts = -1; - int NumLeftover = -1; + unsigned NumParts = 0; LLT LeftoverTy; - SmallVector NarrowRegs, NarrowLeftoverRegs; + SmallVector NarrowRegs; + Register NarrowLeftoverReg; if (IsLoad) { - std::tie(NumParts, NumLeftover) = getNarrowTypeBreakDown(ValTy, NarrowTy, LeftoverTy); - } else { - if (extractParts(ValReg, ValTy, NarrowTy, LeftoverTy, NarrowRegs, - NarrowLeftoverRegs)) { - NumParts = NarrowRegs.size(); - NumLeftover = NarrowLeftoverRegs.size(); - } - } - - if (NumParts == -1) + NumParts = getNarrowTypeBreakDown(ValTy, NarrowTy, LeftoverTy); + NarrowRegs.resize(NumParts); + } else if (extractParts(ValReg, ValTy, NarrowTy, NarrowRegs, LeftoverTy, + NarrowLeftoverReg)) + NumParts = NarrowRegs.size(); + if (!NumParts) return UnableToLegalize; const LLT OffsetTy = LLT::scalar(MRI.getType(AddrReg).getScalarSizeInBits()); @@ -3178,7 +3164,7 @@ LegalizerHelper::reduceLoadStoreWidth(MachineInstr &MI, unsigned TypeIdx, // is a load, return the new registers in ValRegs. For a store, each elements // of ValRegs should be PartTy. Returns the next offset that needs to be // handled. - auto splitTypePieces = [=](LLT PartTy, SmallVectorImpl &ValRegs, + auto splitTypePieces = [=](LLT PartTy, MutableArrayRef ValRegs, unsigned Offset) -> unsigned { MachineFunction &MF = MIRBuilder.getMF(); unsigned PartSize = PartTy.getSizeInBits(); @@ -3195,7 +3181,7 @@ LegalizerHelper::reduceLoadStoreWidth(MachineInstr &MI, unsigned TypeIdx, if (IsLoad) { Register Dst = MRI.createGenericVirtualRegister(PartTy); - ValRegs.push_back(Dst); + ValRegs[Idx] = Dst; MIRBuilder.buildLoad(Dst, NewAddrReg, *NewMMO); } else { MIRBuilder.buildStore(ValRegs[Idx], NewAddrReg, *NewMMO); @@ -3209,12 +3195,11 @@ LegalizerHelper::reduceLoadStoreWidth(MachineInstr &MI, unsigned TypeIdx, // Handle the rest of the register if this isn't an even type breakdown. if (LeftoverTy.isValid()) - splitTypePieces(LeftoverTy, NarrowLeftoverRegs, HandledOffset); + splitTypePieces(LeftoverTy, NarrowLeftoverReg, HandledOffset); - if (IsLoad) { - insertParts(ValReg, ValTy, NarrowTy, NarrowRegs, - LeftoverTy, NarrowLeftoverRegs); - } + if (IsLoad) + insertParts(ValReg, ValTy, NarrowTy, NarrowRegs, LeftoverTy, + NarrowLeftoverReg); MI.eraseFromParent(); return Legalized; @@ -3945,23 +3930,23 @@ LegalizerHelper::narrowScalarInsert(MachineInstr &MI, unsigned TypeIdx, if (TypeIdx != 0) return UnableToLegalize; - uint64_t SizeOp0 = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits(); - uint64_t NarrowSize = NarrowTy.getSizeInBits(); + unsigned DstSize = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits(); + unsigned NarrowSize = NarrowTy.getSizeInBits(); - // FIXME: add support for when SizeOp0 isn't an exact multiple of + // FIXME: add support for when DstSize isn't an exact multiple of // NarrowSize. - if (SizeOp0 % NarrowSize != 0) + if (DstSize % NarrowSize != 0) return UnableToLegalize; - int NumParts = SizeOp0 / NarrowSize; + int NumParts = DstSize / NarrowSize; SmallVector SrcRegs, DstRegs; SmallVector Indexes; extractParts(MI.getOperand(1).getReg(), NarrowTy, NumParts, SrcRegs); Register OpReg = MI.getOperand(2).getReg(); - uint64_t OpStart = MI.getOperand(3).getImm(); - uint64_t OpSize = MRI.getType(OpReg).getSizeInBits(); + unsigned OpStart = MI.getOperand(3).getImm(); + unsigned OpSize = MRI.getType(OpReg).getSizeInBits(); for (int i = 0; i < NumParts; ++i) { unsigned DstStart = i * NarrowSize; @@ -3978,8 +3963,8 @@ LegalizerHelper::narrowScalarInsert(MachineInstr &MI, unsigned TypeIdx, // OpSegStart is where this destination segment would start in OpReg if it // extended infinitely in both directions. - int64_t ExtractOffset, InsertOffset; - uint64_t SegSize; + unsigned ExtractOffset, InsertOffset; + unsigned SegSize; if (OpStart < DstStart) { InsertOffset = 0; ExtractOffset = DstStart - OpStart; @@ -4021,35 +4006,106 @@ LegalizerHelper::narrowScalarBasic(MachineInstr &MI, unsigned TypeIdx, assert(MI.getNumOperands() == 3 && TypeIdx == 0); - SmallVector DstRegs, DstLeftoverRegs; - SmallVector Src0Regs, Src0LeftoverRegs; - SmallVector Src1Regs, Src1LeftoverRegs; + SmallVector DstRegs, Src1Regs, Src2Regs; LLT LeftoverTy; - if (!extractParts(MI.getOperand(1).getReg(), DstTy, NarrowTy, LeftoverTy, - Src0Regs, Src0LeftoverRegs)) + Register DstLeftoverReg, Src1LeftoverReg, Src2LeftoverReg; + if (!extractParts(MI.getOperand(1).getReg(), DstTy, NarrowTy, Src1Regs, + LeftoverTy, Src1LeftoverReg) || + !extractParts(MI.getOperand(2).getReg(), DstTy, NarrowTy, Src2Regs, + LeftoverTy, Src2LeftoverReg)) + return UnableToLegalize; + + for (unsigned I = 0, E = Src1Regs.size(); I != E; ++I) + DstRegs.push_back( + MIRBuilder.buildInstr(MI.getOpcode(), {NarrowTy}, + {Src1Regs[I], Src2Regs[I]}).getReg(0)); + + if (LeftoverTy.isValid()) + DstLeftoverReg = + MIRBuilder.buildInstr(MI.getOpcode(), {LeftoverTy}, + {Src1LeftoverReg, Src2LeftoverReg}).getReg(0); + + insertParts(DstReg, DstTy, NarrowTy, DstRegs, LeftoverTy, DstLeftoverReg); + + MI.eraseFromParent(); + return Legalized; +} + +LegalizerHelper::LegalizeResult +LegalizerHelper::narrowScalarExtended(MachineInstr &MI, unsigned TypeIdx, + LLT NarrowTy) { + if (TypeIdx != 0) return UnableToLegalize; - LLT Unused; - if (!extractParts(MI.getOperand(2).getReg(), DstTy, NarrowTy, Unused, - Src1Regs, Src1LeftoverRegs)) - llvm_unreachable("inconsistent extractParts result"); + unsigned Opc = MI.getOpcode(), OverflowOpc, ExtendOpc; + Register DstReg, ExtendOutReg, Src1Reg, Src2Reg, ExtendInReg; - for (unsigned I = 0, E = Src1Regs.size(); I != E; ++I) { - auto Inst = MIRBuilder.buildInstr(MI.getOpcode(), {NarrowTy}, - {Src0Regs[I], Src1Regs[I]}); - DstRegs.push_back(Inst.getReg(0)); + switch (Opc) { + case TargetOpcode::G_ADD: + case TargetOpcode::G_UADDO: + case TargetOpcode::G_UADDE: + OverflowOpc = TargetOpcode::G_UADDO; + ExtendOpc = TargetOpcode::G_UADDE; + break; + case TargetOpcode::G_SUB: + case TargetOpcode::G_USUBO: + case TargetOpcode::G_USUBE: + OverflowOpc = TargetOpcode::G_USUBO; + ExtendOpc = TargetOpcode::G_USUBE; + break; + default: + llvm_unreachable("Unexpected opcode"); } - for (unsigned I = 0, E = Src1LeftoverRegs.size(); I != E; ++I) { - auto Inst = MIRBuilder.buildInstr( - MI.getOpcode(), - {LeftoverTy}, {Src0LeftoverRegs[I], Src1LeftoverRegs[I]}); - DstLeftoverRegs.push_back(Inst.getReg(0)); + { + unsigned I = 0; + DstReg = MI.getOperand(I++).getReg(); + if (Opc != TargetOpcode::G_ADD && Opc != TargetOpcode::G_SUB) + ExtendOutReg = MI.getOperand(I++).getReg(); + Src1Reg = MI.getOperand(I++).getReg(); + Src2Reg = MI.getOperand(I++).getReg(); + if (Opc == TargetOpcode::G_UADDE || Opc == TargetOpcode::G_USUBE) + ExtendInReg = MI.getOperand(I++).getReg(); } + LLT OpTy = MRI.getType(DstReg); + + LLT LeftoverTy; + SmallVector DstRegs, Src1Regs, Src2Regs; + Register DstLeftoverReg, Src1LeftoverReg, Src2LeftoverReg; - insertParts(DstReg, DstTy, NarrowTy, DstRegs, - LeftoverTy, DstLeftoverRegs); + if (!extractParts(Src1Reg, OpTy, NarrowTy, Src1Regs, LeftoverTy, + Src1LeftoverReg) || + !extractParts(Src2Reg, OpTy, NarrowTy, Src2Regs, LeftoverTy, + Src2LeftoverReg)) + return UnableToLegalize; + unsigned NumParts = Src1Regs.size(); + unsigned TotalParts = NumParts + LeftoverTy.isValid(); + for (unsigned I = 0; I != TotalParts; ++I) { + bool Leftover = I == NumParts; + LLT PartTy = Leftover ? LeftoverTy : NarrowTy; + Register PartDstReg = MRI.createGenericVirtualRegister(PartTy); + Register PartExtendOutReg; + if (I == TotalParts - 1) + PartExtendOutReg = ExtendOutReg; + if (!PartExtendOutReg.isValid()) + PartExtendOutReg = MRI.createGenericVirtualRegister(LLT::scalar(1)); + Register PartSrc1Reg = Leftover ? Src1LeftoverReg : Src1Regs[I]; + Register PartSrc2Reg = Leftover ? Src2LeftoverReg : Src2Regs[I]; + if (ExtendInReg.isValid()) + MIRBuilder.buildInstr(ExtendOpc, {PartDstReg, PartExtendOutReg}, + {PartSrc1Reg, PartSrc2Reg, ExtendInReg}); + else + MIRBuilder.buildInstr(OverflowOpc, {PartDstReg, PartExtendOutReg}, + {PartSrc1Reg, PartSrc2Reg}); + if (Leftover) + DstLeftoverReg = PartDstReg; + else + DstRegs.push_back(PartDstReg); + ExtendInReg = PartExtendOutReg; + } + + insertParts(DstReg, OpTy, NarrowTy, DstRegs, LeftoverTy, DstLeftoverReg); MI.eraseFromParent(); return Legalized; } @@ -4060,18 +4116,61 @@ LegalizerHelper::narrowScalarExt(MachineInstr &MI, unsigned TypeIdx, if (TypeIdx != 0) return UnableToLegalize; + unsigned Opc = MI.getOpcode(); Register DstReg = MI.getOperand(0).getReg(); Register SrcReg = MI.getOperand(1).getReg(); LLT DstTy = MRI.getType(DstReg); - if (DstTy.isVector()) + LLT SrcTy = MRI.getType(SrcReg); + if (DstTy.isVector() || SrcTy.isVector()) return UnableToLegalize; - SmallVector Parts; - LLT GCDTy = extractGCDType(Parts, DstTy, NarrowTy, SrcReg); - LLT LCMTy = buildLCMMergePieces(DstTy, NarrowTy, GCDTy, Parts, MI.getOpcode()); - buildWidenedRemergeToDst(DstReg, LCMTy, Parts); + unsigned DstSize = DstTy.getSizeInBits(); + unsigned SrcSize = SrcTy.getSizeInBits(); + unsigned NarrowSize = NarrowTy.getSizeInBits(); + + SmallVector PartRegs; + LLT LeftoverTy; + Register LeftoverReg; + if (SrcSize <= NarrowSize) + PartRegs.push_back( + MIRBuilder.buildExtOrTrunc(Opc, NarrowTy, SrcReg).getReg(0)); + else if (!extractParts(SrcReg, SrcTy, NarrowTy, PartRegs, LeftoverTy, + LeftoverReg)) + return UnableToLegalize; + + unsigned NumParts = DstSize / NarrowSize; + assert(NumParts >= PartRegs.size() && "Expected sext to increase size"); + if (LeftoverTy.isValid() && NumParts == PartRegs.size()) { + LeftoverTy = LLT::scalar(DstSize - NarrowSize * NumParts); + LeftoverReg = + MIRBuilder.buildInstr(Opc, {LeftoverTy}, {LeftoverReg}).getReg(0); + } else { + if (LeftoverTy.isValid()) + PartRegs.push_back( + MIRBuilder.buildInstr(Opc, {NarrowTy}, {LeftoverReg}).getReg(0)); + + Register ExtReg; + if (Opc == TargetOpcode::G_SEXT) { + auto ShiftAmt = MIRBuilder.buildConstant(NarrowTy, NarrowSize - 1); + ExtReg = + MIRBuilder.buildAShr(NarrowTy, PartRegs.back(), ShiftAmt).getReg(0); + } else if (Opc == TargetOpcode::G_ZEXT) + ExtReg = MIRBuilder.buildConstant(NarrowTy, 0).getReg(0); + else + ExtReg = MIRBuilder.buildUndef(NarrowTy).getReg(0); + while (NumParts > PartRegs.size()) + PartRegs.push_back(ExtReg); + if (DstSize > NumParts * NarrowSize) { + LeftoverTy = LLT::scalar(DstSize - NarrowSize * NumParts); + LeftoverReg = MIRBuilder.buildTrunc(LeftoverTy, ExtReg).getReg(0); + } else { + LeftoverTy = LLT(); + LeftoverReg = Register(); + } + } + insertParts(DstReg, DstTy, NarrowTy, PartRegs, LeftoverTy, LeftoverReg); MI.eraseFromParent(); return Legalized; } @@ -4090,33 +4189,27 @@ LegalizerHelper::narrowScalarSelect(MachineInstr &MI, unsigned TypeIdx, Register DstReg = MI.getOperand(0).getReg(); LLT DstTy = MRI.getType(DstReg); - SmallVector DstRegs, DstLeftoverRegs; - SmallVector Src1Regs, Src1LeftoverRegs; - SmallVector Src2Regs, Src2LeftoverRegs; + SmallVector DstRegs, Src1Regs, Src2Regs; + Register DstLeftoverReg, Src1LeftoverReg, Src2LeftoverReg; LLT LeftoverTy; - if (!extractParts(MI.getOperand(2).getReg(), DstTy, NarrowTy, LeftoverTy, - Src1Regs, Src1LeftoverRegs)) + if (!extractParts(MI.getOperand(2).getReg(), DstTy, NarrowTy, Src1Regs, + LeftoverTy, Src1LeftoverReg) || + !extractParts(MI.getOperand(3).getReg(), DstTy, NarrowTy, Src2Regs, + LeftoverTy, Src2LeftoverReg)) return UnableToLegalize; - LLT Unused; - if (!extractParts(MI.getOperand(3).getReg(), DstTy, NarrowTy, Unused, - Src2Regs, Src2LeftoverRegs)) - llvm_unreachable("inconsistent extractParts result"); - - for (unsigned I = 0, E = Src1Regs.size(); I != E; ++I) { - auto Select = MIRBuilder.buildSelect(NarrowTy, - CondReg, Src1Regs[I], Src2Regs[I]); - DstRegs.push_back(Select.getReg(0)); - } + for (unsigned I = 0, E = Src1Regs.size(); I != E; ++I) + DstRegs.push_back( + MIRBuilder.buildSelect(NarrowTy, CondReg, Src1Regs[I], Src2Regs[I]) + .getReg(0)); - for (unsigned I = 0, E = Src1LeftoverRegs.size(); I != E; ++I) { - auto Select = MIRBuilder.buildSelect( - LeftoverTy, CondReg, Src1LeftoverRegs[I], Src2LeftoverRegs[I]); - DstLeftoverRegs.push_back(Select.getReg(0)); - } + if (LeftoverTy.isValid()) + DstLeftoverReg = + MIRBuilder + .buildSelect(LeftoverTy, CondReg, Src1LeftoverReg, Src2LeftoverReg) + .getReg(0); - insertParts(DstReg, DstTy, NarrowTy, DstRegs, - LeftoverTy, DstLeftoverRegs); + insertParts(DstReg, DstTy, NarrowTy, DstRegs, LeftoverTy, DstLeftoverReg); MI.eraseFromParent(); return Legalized; @@ -5217,6 +5310,47 @@ LegalizerHelper::lowerSADDO_SSUBO(MachineInstr &MI) { return Legalized; } +LegalizerHelper::LegalizeResult +LegalizerHelper::lowerUADDSAT_USUBSAT(MachineInstr &MI) { + Register Dst = MI.getOperand(0).getReg(); + Register LHS = MI.getOperand(1).getReg(); + Register RHS = MI.getOperand(2).getReg(); + const bool IsAdd = MI.getOpcode() == TargetOpcode::G_UADDSAT; + + LLT Ty = MRI.getType(Dst); + unsigned TySize = Ty.getScalarSizeInBits(); + auto Sat = MIRBuilder.buildConstant(Ty, IsAdd ? APInt::getMaxValue(TySize) + : APInt::getMinValue(TySize)); + auto Res = MIRBuilder.buildInstr(IsAdd ? TargetOpcode::G_UADDO + : TargetOpcode::G_USUBO, + {Ty, LLT::scalar(1)}, {LHS, RHS}); + MIRBuilder.buildSelect(Dst, Res.getReg(1), Sat, Res); + MI.eraseFromParent(); + return Legalized; +} + +LegalizerHelper::LegalizeResult +LegalizerHelper::lowerSADDSAT_SSUBSAT(MachineInstr &MI) { + Register Dst = MI.getOperand(0).getReg(); + Register LHS = MI.getOperand(1).getReg(); + Register RHS = MI.getOperand(2).getReg(); + const bool IsAdd = MI.getOpcode() == TargetOpcode::G_SADDSAT; + + LLT Ty = MRI.getType(Dst); + unsigned TySize = Ty.getScalarSizeInBits(); + auto Zero = MIRBuilder.buildConstant(Ty, APInt::getMinValue(TySize)); + auto Min = MIRBuilder.buildConstant(Ty, APInt::getSignedMinValue(TySize)); + auto Max = MIRBuilder.buildConstant(Ty, APInt::getSignedMaxValue(TySize)); + auto Res = MIRBuilder.buildInstr(IsAdd ? TargetOpcode::G_SADDO + : TargetOpcode::G_SSUBO, + {Ty, LLT::scalar(1)}, {LHS, RHS}); + auto Neg = MIRBuilder.buildICmp(CmpInst::ICMP_SLT, LLT::scalar(1), Res, Zero); + auto Sat = MIRBuilder.buildSelect(Ty, Neg, Max, Min); + MIRBuilder.buildSelect(Dst, Res.getReg(1), Sat, Res); + MI.eraseFromParent(); + return Legalized; +} + LegalizerHelper::LegalizeResult LegalizerHelper::lowerBswap(MachineInstr &MI) { Register Dst = MI.getOperand(0).getReg(); From 649598944031c6f9bc7b897bf2191273add4b7b6 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 14 Nov 2019 01:33:31 +0100 Subject: [PATCH 06/21] [GlobalISel][ArtifactCombiner] Bail out on unhandled case. --- .../llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h index f31c88131cf3ec..757288e0dceebb 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h @@ -536,7 +536,10 @@ class LegalizationArtifactCombiner { const unsigned NumMergeRegs = MergeI->getNumOperands() - 1; if (NumMergeRegs < NumDefs) { - if (NumDefs % NumMergeRegs != 0) + if (NumMergeRegs % NumDefs != 0) + return false; + + if (ConvertOp && !MRI.getType(MergeI->getOperand(0).getReg()).isVector()) return false; Builder.setInstr(MI); From 46f9733bf734bab43fdcbbaa433acb8c810861d8 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 13 Nov 2019 21:36:03 +0100 Subject: [PATCH 07/21] [Bugpoint] Don't give up completely when an invalid module gets generated, by default. --- llvm/tools/bugpoint/CrashDebugger.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/llvm/tools/bugpoint/CrashDebugger.cpp b/llvm/tools/bugpoint/CrashDebugger.cpp index 1a39ff654f0563..d06c0b83e1d2d2 100644 --- a/llvm/tools/bugpoint/CrashDebugger.cpp +++ b/llvm/tools/bugpoint/CrashDebugger.cpp @@ -68,6 +68,10 @@ cl::opt NoStripDebugTypeInfo("disable-strip-debug-types", cl::opt VerboseErrors("verbose-errors", cl::desc("Print the output of crashing program"), cl::init(false)); +cl::opt VerifyModule("verify-module", + cl::desc("Verify modules before running tests and " + "ignore those that fail verification"), + cl::init(true)); } namespace llvm { @@ -1409,7 +1413,9 @@ Error BugDriver::debugOptimizerCrash(const std::string &ID) { } static bool TestForCodeGenCrash(const BugDriver &BD, Module *M) { - if (Error E = BD.compileProgram(*M)) { + if (VerifyModule && verifyModule(*M)) + errs() << ""; + else if (Error E = BD.compileProgram(*M)) { if (VerboseErrors) errs() << toString(std::move(E)) << "\n"; else { From 16dada5678bbc9898b077126899d572b10cd4aeb Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 20 Oct 2019 20:08:22 +0200 Subject: [PATCH 08/21] Update .gitignore and fix emacs.el. --- .gitignore | 1 + llvm/.gitignore | 2 ++ llvm/utils/emacs/emacs.el | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 43ff4794b019f3..d844e23aceee19 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ #==============================================================================# # Temp files created by most text editors. *~ +\#*# # Merge files created by git. *.orig # Byte compiled python modules. diff --git a/llvm/.gitignore b/llvm/.gitignore index f2b343ade4f321..d51c02523be885 100644 --- a/llvm/.gitignore +++ b/llvm/.gitignore @@ -74,8 +74,10 @@ bindings/go/llvm/workdir #==============================================================================# # Temp files created by most text editors. *~ +#*# # Merge files created by git. *.orig +*.rej # Byte compiled python modules. *.pyc # vim swap files diff --git a/llvm/utils/emacs/emacs.el b/llvm/utils/emacs/emacs.el index 3a2b47cee1db13..e41119923fdcb1 100644 --- a/llvm/utils/emacs/emacs.el +++ b/llvm/utils/emacs/emacs.el @@ -27,6 +27,7 @@ (add-hook 'c-mode-common-hook (function (lambda nil - (if (string-match "llvm" buffer-file-name) + (if (and buffer-file-name + (string-match "llvm" buffer-file-name)) (progn (c-set-style "llvm.org")))))) From 8c9b181141e510a9b1f416d726b2740f125f6110 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 12 Apr 2020 21:25:46 -0400 Subject: [PATCH 09/21] [cc1] Initialize all of the passes when -mllvm is passed so that -print-before/after can be parsed correctly. --- clang/tools/driver/cc1_main.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp index 0872015e0ab051..2fb25e223c9766 100644 --- a/clang/tools/driver/cc1_main.cpp +++ b/clang/tools/driver/cc1_main.cpp @@ -27,10 +27,12 @@ #include "clang/FrontendTool/Utils.h" #include "llvm/ADT/Statistic.h" #include "llvm/Config/llvm-config.h" +#include "llvm/InitializePasses.h" #include "llvm/LinkAllPasses.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/OptTable.h" +#include "llvm/Pass.h" #include "llvm/Support/BuryPointer.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" @@ -234,6 +236,26 @@ int cc1_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { if (!Success) return 1; + if (!Clang->getFrontendOpts().LLVMArgs.empty()) { + // Initialize codegen and IR passes used by llc so that the -print-after, + // -print-before, and -stop-after options work. + llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry(); + llvm::initializeCore(Registry); + llvm::initializeCodeGen(Registry); + llvm::initializeLoopStrengthReducePass(Registry); + llvm::initializeLowerIntrinsicsPass(Registry); + llvm::initializeEntryExitInstrumenterPass(Registry); + llvm::initializePostInlineEntryExitInstrumenterPass(Registry); + llvm::initializeUnreachableBlockElimLegacyPassPass(Registry); + llvm::initializeConstantHoistingLegacyPassPass(Registry); + llvm::initializeScalarOpts(Registry); + llvm::initializeVectorization(Registry); + llvm::initializeScalarizeMaskedMemIntrinPass(Registry); + llvm::initializeExpandReductionsPass(Registry); + llvm::initializeHardwareLoopsPass(Registry); + llvm::initializeTransformUtils(Registry); + } + // Execute the frontend actions. { llvm::TimeTraceScope TimeScope("ExecuteCompiler"); From acf4d1d92b16fbb1a761958ae5a9825db13b44bf Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 19 Oct 2019 23:04:13 +0200 Subject: [PATCH 10/21] Add i24 simple value type and fix resulting target breakage. --- llvm/include/llvm/CodeGen/TargetLowering.h | 2 +- llvm/include/llvm/CodeGen/ValueTypes.td | 323 ++++++++--------- llvm/include/llvm/Support/LowLevelTypeImpl.h | 2 +- llvm/include/llvm/Support/MachineValueType.h | 340 ++++++++++-------- llvm/lib/CodeGen/TargetLoweringBase.cpp | 47 ++- llvm/lib/CodeGen/ValueTypes.cpp | 1 + llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp | 2 +- llvm/lib/Target/AMDGPU/R600ISelLowering.cpp | 3 + llvm/lib/Target/AVR/AVRISelLowering.cpp | 3 + llvm/lib/Target/X86/X86FastISel.cpp | 17 +- llvm/lib/Target/X86/X86ISelLowering.cpp | 2 +- llvm/utils/TableGen/CodeGenTarget.cpp | 1 + 12 files changed, 401 insertions(+), 342 deletions(-) diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h index f920d3016a95b5..3af91edb81c40d 100644 --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -1649,7 +1649,7 @@ class TargetLoweringBase { /// example, on X86 targets without SSE2 f64 load / store are done with fldl / /// fstpl which also does type conversion. Note the specified type doesn't /// have to be legal as the hook is used before type legalization. - virtual bool isSafeMemOpType(MVT /*VT*/) const { return true; } + virtual bool isSafeMemOpType(MVT VT) const { return VT.isPow2Size(); } /// Return lower limit for number of blocks in a jump table. virtual unsigned getMinimumJumpTableEntries() const; diff --git a/llvm/include/llvm/CodeGen/ValueTypes.td b/llvm/include/llvm/CodeGen/ValueTypes.td index dfa2e81608be8c..8ddd4659c8fa50 100644 --- a/llvm/include/llvm/CodeGen/ValueTypes.td +++ b/llvm/include/llvm/CodeGen/ValueTypes.td @@ -22,167 +22,168 @@ def OtherVT: ValueType<0 , 1>; // "Other" value def i1 : ValueType<1 , 2>; // One bit boolean value def i8 : ValueType<8 , 3>; // 8-bit integer value def i16 : ValueType<16 , 4>; // 16-bit integer value -def i32 : ValueType<32 , 5>; // 32-bit integer value -def i64 : ValueType<64 , 6>; // 64-bit integer value -def i128 : ValueType<128, 7>; // 128-bit integer value - -def bf16 : ValueType<16 , 8>; // 16-bit brain floating point value -def f16 : ValueType<16 , 9>; // 16-bit floating point value -def f32 : ValueType<32 , 10>; // 32-bit floating point value -def f64 : ValueType<64 , 11>; // 64-bit floating point value -def f80 : ValueType<80 , 12>; // 80-bit floating point value -def f128 : ValueType<128, 13>; // 128-bit floating point value -def ppcf128: ValueType<128, 14>; // PPC 128-bit floating point value - -def v1i1 : ValueType<1 , 15>; // 1 x i1 vector value -def v2i1 : ValueType<2 , 16>; // 2 x i1 vector value -def v4i1 : ValueType<4 , 17>; // 4 x i1 vector value -def v8i1 : ValueType<8 , 18>; // 8 x i1 vector value -def v16i1 : ValueType<16, 19>; // 16 x i1 vector value -def v32i1 : ValueType<32 , 20>; // 32 x i1 vector value -def v64i1 : ValueType<64 , 21>; // 64 x i1 vector value -def v128i1 : ValueType<128, 22>; // 128 x i1 vector value -def v256i1 : ValueType<256, 23>; // 256 x i1 vector value -def v512i1 : ValueType<512, 24>; // 512 x i1 vector value -def v1024i1: ValueType<1024,25>; //1024 x i1 vector value - -def v1i8 : ValueType<8, 26>; // 1 x i8 vector value -def v2i8 : ValueType<16 , 27>; // 2 x i8 vector value -def v4i8 : ValueType<32 , 28>; // 4 x i8 vector value -def v8i8 : ValueType<64 , 29>; // 8 x i8 vector value -def v16i8 : ValueType<128, 30>; // 16 x i8 vector value -def v32i8 : ValueType<256, 31>; // 32 x i8 vector value -def v64i8 : ValueType<512, 32>; // 64 x i8 vector value -def v128i8 : ValueType<1024,33>; //128 x i8 vector value -def v256i8 : ValueType<2048,34>; //256 x i8 vector value - -def v1i16 : ValueType<16 , 35>; // 1 x i16 vector value -def v2i16 : ValueType<32 , 36>; // 2 x i16 vector value -def v3i16 : ValueType<48 , 37>; // 3 x i16 vector value -def v4i16 : ValueType<64 , 38>; // 4 x i16 vector value -def v8i16 : ValueType<128, 39>; // 8 x i16 vector value -def v16i16 : ValueType<256, 40>; // 16 x i16 vector value -def v32i16 : ValueType<512, 41>; // 32 x i16 vector value -def v64i16 : ValueType<1024,42>; // 64 x i16 vector value -def v128i16: ValueType<2048,43>; //128 x i16 vector value - -def v1i32 : ValueType<32 , 44>; // 1 x i32 vector value -def v2i32 : ValueType<64 , 45>; // 2 x i32 vector value -def v3i32 : ValueType<96 , 46>; // 3 x i32 vector value -def v4i32 : ValueType<128, 47>; // 4 x i32 vector value -def v5i32 : ValueType<160, 48>; // 5 x i32 vector value -def v8i32 : ValueType<256, 49>; // 8 x i32 vector value -def v16i32 : ValueType<512, 50>; // 16 x i32 vector value -def v32i32 : ValueType<1024,51>; // 32 x i32 vector value -def v64i32 : ValueType<2048,52>; // 64 x i32 vector value -def v128i32 : ValueType<4096,53>; // 128 x i32 vector value -def v256i32 : ValueType<8182,54>; // 256 x i32 vector value -def v512i32 : ValueType<16384,55>; // 512 x i32 vector value -def v1024i32 : ValueType<32768,56>; // 1024 x i32 vector value -def v2048i32 : ValueType<65536,57>; // 2048 x i32 vector value - -def v1i64 : ValueType<64 , 58>; // 1 x i64 vector value -def v2i64 : ValueType<128, 59>; // 2 x i64 vector value -def v4i64 : ValueType<256, 60>; // 4 x i64 vector value -def v8i64 : ValueType<512, 61>; // 8 x i64 vector value -def v16i64 : ValueType<1024,62>; // 16 x i64 vector value -def v32i64 : ValueType<2048,63>; // 32 x i64 vector value - -def v1i128 : ValueType<128, 64>; // 1 x i128 vector value - -def v2f16 : ValueType<32 , 65>; // 2 x f16 vector value -def v3f16 : ValueType<48 , 66>; // 3 x f16 vector value -def v4f16 : ValueType<64 , 67>; // 4 x f16 vector value -def v8f16 : ValueType<128, 68>; // 8 x f16 vector value -def v16f16 : ValueType<256, 69>; // 16 x f16 vector value -def v32f16 : ValueType<512, 70>; // 32 x f16 vector value -def v64f16 : ValueType<1024, 71>; // 64 x f16 vector value -def v128f16 : ValueType<2048, 72>; // 128 x f16 vector value -def v2bf16 : ValueType<32 , 73>; // 2 x bf16 vector value -def v3bf16 : ValueType<48 , 74>; // 3 x bf16 vector value -def v4bf16 : ValueType<64 , 75>; // 4 x bf16 vector value -def v8bf16 : ValueType<128, 76>; // 8 x bf16 vector value -def v16bf16 : ValueType<256, 77>; // 16 x bf16 vector value -def v32bf16 : ValueType<512, 78>; // 32 x bf16 vector value -def v64bf16 : ValueType<1024, 79>; // 64 x bf16 vector value -def v128bf16 : ValueType<2048, 80>; // 128 x bf16 vector value -def v1f32 : ValueType<32 , 81>; // 1 x f32 vector value -def v2f32 : ValueType<64 , 82>; // 2 x f32 vector value -def v3f32 : ValueType<96 , 83>; // 3 x f32 vector value -def v4f32 : ValueType<128, 84>; // 4 x f32 vector value -def v5f32 : ValueType<160, 85>; // 5 x f32 vector value -def v8f32 : ValueType<256, 86>; // 8 x f32 vector value -def v16f32 : ValueType<512, 87>; // 16 x f32 vector value -def v32f32 : ValueType<1024, 88>; // 32 x f32 vector value -def v64f32 : ValueType<2048, 89>; // 64 x f32 vector value -def v128f32 : ValueType<4096, 90>; // 128 x f32 vector value -def v256f32 : ValueType<8182, 91>; // 256 x f32 vector value -def v512f32 : ValueType<16384, 92>; // 512 x f32 vector value -def v1024f32 : ValueType<32768, 93>; // 1024 x f32 vector value -def v2048f32 : ValueType<65536, 94>; // 2048 x f32 vector value -def v1f64 : ValueType<64, 95>; // 1 x f64 vector value -def v2f64 : ValueType<128, 96>; // 2 x f64 vector value -def v4f64 : ValueType<256, 97>; // 4 x f64 vector value -def v8f64 : ValueType<512, 98>; // 8 x f64 vector value -def v16f64 : ValueType<1024, 99>; // 16 x f64 vector value -def v32f64 : ValueType<2048, 100>; // 32 x f64 vector value - -def nxv1i1 : ValueType<1, 101>; // n x 1 x i1 vector value -def nxv2i1 : ValueType<2, 102>; // n x 2 x i1 vector value -def nxv4i1 : ValueType<4, 103>; // n x 4 x i1 vector value -def nxv8i1 : ValueType<8, 104>; // n x 8 x i1 vector value -def nxv16i1 : ValueType<16, 105>; // n x 16 x i1 vector value -def nxv32i1 : ValueType<32, 106>; // n x 32 x i1 vector value - -def nxv1i8 : ValueType<8, 107>; // n x 1 x i8 vector value -def nxv2i8 : ValueType<16, 108>; // n x 2 x i8 vector value -def nxv4i8 : ValueType<32, 109>; // n x 4 x i8 vector value -def nxv8i8 : ValueType<64, 110>; // n x 8 x i8 vector value -def nxv16i8 : ValueType<128, 111>; // n x 16 x i8 vector value -def nxv32i8 : ValueType<256, 112>; // n x 32 x i8 vector value - -def nxv1i16 : ValueType<16, 113>; // n x 1 x i16 vector value -def nxv2i16 : ValueType<32, 114>; // n x 2 x i16 vector value -def nxv4i16 : ValueType<64, 115>; // n x 4 x i16 vector value -def nxv8i16 : ValueType<128, 116>; // n x 8 x i16 vector value -def nxv16i16: ValueType<256, 117>; // n x 16 x i16 vector value -def nxv32i16: ValueType<512, 118>; // n x 32 x i16 vector value - -def nxv1i32 : ValueType<32, 119>; // n x 1 x i32 vector value -def nxv2i32 : ValueType<64, 120>; // n x 2 x i32 vector value -def nxv4i32 : ValueType<128, 121>; // n x 4 x i32 vector value -def nxv8i32 : ValueType<256, 122>; // n x 8 x i32 vector value -def nxv16i32: ValueType<512, 123>; // n x 16 x i32 vector value -def nxv32i32: ValueType<1024,124>; // n x 32 x i32 vector value - -def nxv1i64 : ValueType<64, 125>; // n x 1 x i64 vector value -def nxv2i64 : ValueType<128, 126>; // n x 2 x i64 vector value -def nxv4i64 : ValueType<256, 127>; // n x 4 x i64 vector value -def nxv8i64 : ValueType<512, 128>; // n x 8 x i64 vector value -def nxv16i64: ValueType<1024,129>; // n x 16 x i64 vector value -def nxv32i64: ValueType<2048,130>; // n x 32 x i64 vector value - -def nxv2f16 : ValueType<32 , 131>; // n x 2 x f16 vector value -def nxv4f16 : ValueType<64 , 132>; // n x 4 x f16 vector value -def nxv8f16 : ValueType<128, 133>; // n x 8 x f16 vector value -def nxv2bf16 : ValueType<32 , 134>; // n x 2 x bf16 vector value -def nxv4bf16 : ValueType<64 , 135>; // n x 4 x bf16 vector value -def nxv8bf16 : ValueType<128, 136>; // n x 8 x bf16 vector value -def nxv1f32 : ValueType<32 , 137>; // n x 1 x f32 vector value -def nxv2f32 : ValueType<64 , 138>; // n x 2 x f32 vector value -def nxv4f32 : ValueType<128, 139>; // n x 4 x f32 vector value -def nxv8f32 : ValueType<256, 140>; // n x 8 x f32 vector value -def nxv16f32 : ValueType<512, 141>; // n x 16 x f32 vector value -def nxv1f64 : ValueType<64, 142>; // n x 1 x f64 vector value -def nxv2f64 : ValueType<128, 143>; // n x 2 x f64 vector value -def nxv4f64 : ValueType<256, 144>; // n x 4 x f64 vector value -def nxv8f64 : ValueType<512, 145>; // n x 8 x f64 vector value - -def x86mmx : ValueType<64 , 146>; // X86 MMX value -def FlagVT : ValueType<0 , 147>; // Pre-RA sched glue -def isVoid : ValueType<0 , 148>; // Produces no value -def untyped: ValueType<8 , 149>; // Produces an untyped value -def exnref : ValueType<0 , 150>; // WebAssembly's exnref type +def i24 : ValueType<24 , 5>; // 24-bit integer value +def i32 : ValueType<32 , 6>; // 32-bit integer value +def i64 : ValueType<64 , 7>; // 64-bit integer value +def i128 : ValueType<128, 8>; // 128-bit integer value + +def bf16 : ValueType<16 , 9>; // 16-bit brain floating point value +def f16 : ValueType<16 , 10>; // 16-bit floating point value +def f32 : ValueType<32 , 11>; // 32-bit floating point value +def f64 : ValueType<64 , 12>; // 64-bit floating point value +def f80 : ValueType<80 , 13>; // 80-bit floating point value +def f128 : ValueType<128, 14>; // 128-bit floating point value +def ppcf128: ValueType<128, 15>; // PPC 128-bit floating point value + +def v1i1 : ValueType<1 , 16>; // 1 x i1 vector value +def v2i1 : ValueType<2 , 17>; // 2 x i1 vector value +def v4i1 : ValueType<4 , 18>; // 4 x i1 vector value +def v8i1 : ValueType<8 , 19>; // 8 x i1 vector value +def v16i1 : ValueType<16, 20>; // 16 x i1 vector value +def v32i1 : ValueType<32 , 21>; // 32 x i1 vector value +def v64i1 : ValueType<64 , 22>; // 64 x i1 vector value +def v128i1 : ValueType<128, 23>; // 128 x i1 vector value +def v256i1 : ValueType<256, 24>; // 256 x i1 vector value +def v512i1 : ValueType<512, 25>; // 512 x i1 vector value +def v1024i1: ValueType<1024,26>; //1024 x i1 vector value + +def v1i8 : ValueType<8, 27>; // 1 x i8 vector value +def v2i8 : ValueType<16 , 28>; // 2 x i8 vector value +def v4i8 : ValueType<32 , 29>; // 4 x i8 vector value +def v8i8 : ValueType<64 , 30>; // 8 x i8 vector value +def v16i8 : ValueType<128, 31>; // 16 x i8 vector value +def v32i8 : ValueType<256, 32>; // 32 x i8 vector value +def v64i8 : ValueType<512, 33>; // 64 x i8 vector value +def v128i8 : ValueType<1024,34>; //128 x i8 vector value +def v256i8 : ValueType<2048,35>; //256 x i8 vector value + +def v1i16 : ValueType<16 , 36>; // 1 x i16 vector value +def v2i16 : ValueType<32 , 37>; // 2 x i16 vector value +def v3i16 : ValueType<48 , 38>; // 3 x i16 vector value +def v4i16 : ValueType<64 , 39>; // 4 x i16 vector value +def v8i16 : ValueType<128, 40>; // 8 x i16 vector value +def v16i16 : ValueType<256, 41>; // 16 x i16 vector value +def v32i16 : ValueType<512, 42>; // 32 x i16 vector value +def v64i16 : ValueType<1024,43>; // 64 x i16 vector value +def v128i16: ValueType<2048,44>; //128 x i16 vector value + +def v1i32 : ValueType<32 , 45>; // 1 x i32 vector value +def v2i32 : ValueType<64 , 46>; // 2 x i32 vector value +def v3i32 : ValueType<96 , 47>; // 3 x i32 vector value +def v4i32 : ValueType<128, 48>; // 4 x i32 vector value +def v5i32 : ValueType<160, 49>; // 5 x i32 vector value +def v8i32 : ValueType<256, 50>; // 8 x i32 vector value +def v16i32 : ValueType<512, 51>; // 16 x i32 vector value +def v32i32 : ValueType<1024,52>; // 32 x i32 vector value +def v64i32 : ValueType<2048,53>; // 64 x i32 vector value +def v128i32 : ValueType<4096,54>; // 128 x i32 vector value +def v256i32 : ValueType<8182,55>; // 256 x i32 vector value +def v512i32 : ValueType<16384,56>; // 512 x i32 vector value +def v1024i32 : ValueType<32768,57>; // 1024 x i32 vector value +def v2048i32 : ValueType<65536,58>; // 2048 x i32 vector value + +def v1i64 : ValueType<64 , 59>; // 1 x i64 vector value +def v2i64 : ValueType<128, 60>; // 2 x i64 vector value +def v4i64 : ValueType<256, 61>; // 4 x i64 vector value +def v8i64 : ValueType<512, 62>; // 8 x i64 vector value +def v16i64 : ValueType<1024,63>; // 16 x i64 vector value +def v32i64 : ValueType<2048,64>; // 32 x i64 vector value + +def v1i128 : ValueType<128, 65>; // 1 x i128 vector value + +def v2f16 : ValueType<32 , 66>; // 2 x f16 vector value +def v3f16 : ValueType<48 , 67>; // 3 x f16 vector value +def v4f16 : ValueType<64 , 68>; // 4 x f16 vector value +def v8f16 : ValueType<128, 69>; // 8 x f16 vector value +def v16f16 : ValueType<256, 70>; // 16 x f16 vector value +def v32f16 : ValueType<512, 71>; // 32 x f16 vector value +def v64f16 : ValueType<1024, 72>; // 64 x f16 vector value +def v128f16 : ValueType<2048, 73>; // 128 x f16 vector value +def v2bf16 : ValueType<32 , 74>; // 2 x bf16 vector value +def v3bf16 : ValueType<48 , 75>; // 3 x bf16 vector value +def v4bf16 : ValueType<64 , 76>; // 4 x bf16 vector value +def v8bf16 : ValueType<128, 77>; // 8 x bf16 vector value +def v16bf16 : ValueType<256, 78>; // 16 x bf16 vector value +def v32bf16 : ValueType<512, 79>; // 32 x bf16 vector value +def v64bf16 : ValueType<1024, 80>; // 64 x bf16 vector value +def v128bf16 : ValueType<2048, 81>; // 128 x bf16 vector value +def v1f32 : ValueType<32 , 82>; // 1 x f32 vector value +def v2f32 : ValueType<64 , 83>; // 2 x f32 vector value +def v3f32 : ValueType<96 , 84>; // 3 x f32 vector value +def v4f32 : ValueType<128, 85>; // 4 x f32 vector value +def v5f32 : ValueType<160, 86>; // 5 x f32 vector value +def v8f32 : ValueType<256, 87>; // 8 x f32 vector value +def v16f32 : ValueType<512, 88>; // 16 x f32 vector value +def v32f32 : ValueType<1024, 89>; // 32 x f32 vector value +def v64f32 : ValueType<2048, 90>; // 64 x f32 vector value +def v128f32 : ValueType<4096, 91>; // 128 x f32 vector value +def v256f32 : ValueType<8182, 92>; // 256 x f32 vector value +def v512f32 : ValueType<16384, 93>; // 512 x f32 vector value +def v1024f32 : ValueType<32768, 94>; // 1024 x f32 vector value +def v2048f32 : ValueType<65536, 95>; // 2048 x f32 vector value +def v1f64 : ValueType<64, 96>; // 1 x f64 vector value +def v2f64 : ValueType<128, 97>; // 2 x f64 vector value +def v4f64 : ValueType<256, 98>; // 4 x f64 vector value +def v8f64 : ValueType<512, 99>; // 8 x f64 vector value +def v16f64 : ValueType<1024,100>; // 16 x f64 vector value +def v32f64 : ValueType<2048, 101>; // 32 x f64 vector value + +def nxv1i1 : ValueType<1, 102>; // n x 1 x i1 vector value +def nxv2i1 : ValueType<2, 103>; // n x 2 x i1 vector value +def nxv4i1 : ValueType<4, 104>; // n x 4 x i1 vector value +def nxv8i1 : ValueType<8, 105>; // n x 8 x i1 vector value +def nxv16i1 : ValueType<16, 106>; // n x 16 x i1 vector value +def nxv32i1 : ValueType<32, 107>; // n x 32 x i1 vector value + +def nxv1i8 : ValueType<8, 108>; // n x 1 x i8 vector value +def nxv2i8 : ValueType<16, 109>; // n x 2 x i8 vector value +def nxv4i8 : ValueType<32, 110>; // n x 4 x i8 vector value +def nxv8i8 : ValueType<64, 111>; // n x 8 x i8 vector value +def nxv16i8 : ValueType<128, 112>; // n x 16 x i8 vector value +def nxv32i8 : ValueType<256, 113>; // n x 32 x i8 vector value + +def nxv1i16 : ValueType<16, 114>; // n x 1 x i16 vector value +def nxv2i16 : ValueType<32, 115>; // n x 2 x i16 vector value +def nxv4i16 : ValueType<64, 116>; // n x 4 x i16 vector value +def nxv8i16 : ValueType<128, 117>; // n x 8 x i16 vector value +def nxv16i16: ValueType<256, 118>; // n x 16 x i16 vector value +def nxv32i16: ValueType<512, 119>; // n x 32 x i16 vector value + +def nxv1i32 : ValueType<32, 120>; // n x 1 x i32 vector value +def nxv2i32 : ValueType<64, 121>; // n x 2 x i32 vector value +def nxv4i32 : ValueType<128, 122>; // n x 4 x i32 vector value +def nxv8i32 : ValueType<256, 123>; // n x 8 x i32 vector value +def nxv16i32: ValueType<512, 124>; // n x 16 x i32 vector value +def nxv32i32: ValueType<1024,125>; // n x 32 x i32 vector value + +def nxv1i64 : ValueType<64, 126>; // n x 1 x i64 vector value +def nxv2i64 : ValueType<128, 127>; // n x 2 x i64 vector value +def nxv4i64 : ValueType<256, 128>; // n x 4 x i64 vector value +def nxv8i64 : ValueType<512, 129>; // n x 8 x i64 vector value +def nxv16i64: ValueType<1024,130>; // n x 16 x i64 vector value +def nxv32i64: ValueType<2048,131>; // n x 32 x i64 vector value + +def nxv2f16 : ValueType<32 , 132>; // n x 2 x f16 vector value +def nxv4f16 : ValueType<64 , 133>; // n x 4 x f16 vector value +def nxv8f16 : ValueType<128, 134>; // n x 8 x f16 vector value +def nxv2bf16 : ValueType<32 , 135>; // n x 2 x bf16 vector value +def nxv4bf16 : ValueType<64 , 136>; // n x 4 x bf16 vector value +def nxv8bf16 : ValueType<128, 137>; // n x 8 x bf16 vector value +def nxv1f32 : ValueType<32 , 138>; // n x 1 x f32 vector value +def nxv2f32 : ValueType<64 , 139>; // n x 2 x f32 vector value +def nxv4f32 : ValueType<128, 140>; // n x 4 x f32 vector value +def nxv8f32 : ValueType<256, 141>; // n x 8 x f32 vector value +def nxv16f32 : ValueType<512, 142>; // n x 16 x f32 vector value +def nxv1f64 : ValueType<64, 143>; // n x 1 x f64 vector value +def nxv2f64 : ValueType<128, 144>; // n x 2 x f64 vector value +def nxv4f64 : ValueType<256, 145>; // n x 4 x f64 vector value +def nxv8f64 : ValueType<512, 146>; // n x 8 x f64 vector value + +def x86mmx : ValueType<64 , 147>; // X86 MMX value +def FlagVT : ValueType<0 , 148>; // Pre-RA sched glue +def isVoid : ValueType<0 , 149>; // Produces no value +def untyped: ValueType<8 , 150>; // Produces an untyped value +def exnref : ValueType<0 , 151>; // WebAssembly's exnref type def token : ValueType<0 , 248>; // TokenTy def MetadataVT: ValueType<0, 249>; // Metadata diff --git a/llvm/include/llvm/Support/LowLevelTypeImpl.h b/llvm/include/llvm/Support/LowLevelTypeImpl.h index c1d516f2fe587a..9d5df0870d5f83 100644 --- a/llvm/include/llvm/Support/LowLevelTypeImpl.h +++ b/llvm/include/llvm/Support/LowLevelTypeImpl.h @@ -115,7 +115,7 @@ class LLT { /// Returns the total size of the type in bytes, i.e. number of whole bytes /// needed to represent the size in bits. Must only be called on sized types. unsigned getSizeInBytes() const { - return (getSizeInBits() + 7) / 8; + return divideCeil(getSizeInBits(), 8); } LLT getScalarType() const { diff --git a/llvm/include/llvm/Support/MachineValueType.h b/llvm/include/llvm/Support/MachineValueType.h index e878915868758e..c10c3ac47cac0a 100644 --- a/llvm/include/llvm/Support/MachineValueType.h +++ b/llvm/include/llvm/Support/MachineValueType.h @@ -40,119 +40,120 @@ namespace llvm { i1 = 2, // This is a 1 bit integer value i8 = 3, // This is an 8 bit integer value i16 = 4, // This is a 16 bit integer value - i32 = 5, // This is a 32 bit integer value - i64 = 6, // This is a 64 bit integer value - i128 = 7, // This is a 128 bit integer value + i24 = 5, // This is a 24 bit integer value + i32 = 6, // This is a 32 bit integer value + i64 = 7, // This is a 64 bit integer value + i128 = 8, // This is a 128 bit integer value FIRST_INTEGER_VALUETYPE = i1, LAST_INTEGER_VALUETYPE = i128, - bf16 = 8, // This is a 16 bit brain floating point value - f16 = 9, // This is a 16 bit floating point value - f32 = 10, // This is a 32 bit floating point value - f64 = 11, // This is a 64 bit floating point value - f80 = 12, // This is a 80 bit floating point value - f128 = 13, // This is a 128 bit floating point value - ppcf128 = 14, // This is a PPC 128-bit floating point value + bf16 = 9, // This is a 16 bit brain floating point value + f16 = 10, // This is a 16 bit floating point value + f32 = 11, // This is a 32 bit floating point value + f64 = 12, // This is a 64 bit floating point value + f80 = 13, // This is a 80 bit floating point value + f128 = 14, // This is a 128 bit floating point value + ppcf128 = 15, // This is a PPC 128-bit floating point value FIRST_FP_VALUETYPE = bf16, LAST_FP_VALUETYPE = ppcf128, - v1i1 = 15, // 1 x i1 - v2i1 = 16, // 2 x i1 - v4i1 = 17, // 4 x i1 - v8i1 = 18, // 8 x i1 - v16i1 = 19, // 16 x i1 - v32i1 = 20, // 32 x i1 - v64i1 = 21, // 64 x i1 - v128i1 = 22, // 128 x i1 - v256i1 = 23, // 256 x i1 - v512i1 = 24, // 512 x i1 - v1024i1 = 25, // 1024 x i1 - - v1i8 = 26, // 1 x i8 - v2i8 = 27, // 2 x i8 - v4i8 = 28, // 4 x i8 - v8i8 = 29, // 8 x i8 - v16i8 = 30, // 16 x i8 - v32i8 = 31, // 32 x i8 - v64i8 = 32, // 64 x i8 - v128i8 = 33, //128 x i8 - v256i8 = 34, //256 x i8 - - v1i16 = 35, // 1 x i16 - v2i16 = 36, // 2 x i16 - v3i16 = 37, // 3 x i16 - v4i16 = 38, // 4 x i16 - v8i16 = 39, // 8 x i16 - v16i16 = 40, // 16 x i16 - v32i16 = 41, // 32 x i16 - v64i16 = 42, // 64 x i16 - v128i16 = 43, //128 x i16 - - v1i32 = 44, // 1 x i32 - v2i32 = 45, // 2 x i32 - v3i32 = 46, // 3 x i32 - v4i32 = 47, // 4 x i32 - v5i32 = 48, // 5 x i32 - v8i32 = 49, // 8 x i32 - v16i32 = 50, // 16 x i32 - v32i32 = 51, // 32 x i32 - v64i32 = 52, // 64 x i32 - v128i32 = 53, // 128 x i32 - v256i32 = 54, // 256 x i32 - v512i32 = 55, // 512 x i32 - v1024i32 = 56, // 1024 x i32 - v2048i32 = 57, // 2048 x i32 - - v1i64 = 58, // 1 x i64 - v2i64 = 59, // 2 x i64 - v4i64 = 60, // 4 x i64 - v8i64 = 61, // 8 x i64 - v16i64 = 62, // 16 x i64 - v32i64 = 63, // 32 x i64 - - v1i128 = 64, // 1 x i128 + v1i1 = 16, // 1 x i1 + v2i1 = 17, // 2 x i1 + v4i1 = 18, // 4 x i1 + v8i1 = 19, // 8 x i1 + v16i1 = 20, // 16 x i1 + v32i1 = 21, // 32 x i1 + v64i1 = 22, // 64 x i1 + v128i1 = 23, // 128 x i1 + v256i1 = 24, // 256 x i1 + v512i1 = 25, // 512 x i1 + v1024i1 = 26, // 1024 x i1 + + v1i8 = 27, // 1 x i8 + v2i8 = 28, // 2 x i8 + v4i8 = 29, // 4 x i8 + v8i8 = 30, // 8 x i8 + v16i8 = 31, // 16 x i8 + v32i8 = 32, // 32 x i8 + v64i8 = 33, // 64 x i8 + v128i8 = 34, //128 x i8 + v256i8 = 35, //256 x i8 + + v1i16 = 36, // 1 x i16 + v2i16 = 37, // 2 x i16 + v3i16 = 38, // 3 x i16 + v4i16 = 39, // 4 x i16 + v8i16 = 40, // 8 x i16 + v16i16 = 41, // 16 x i16 + v32i16 = 42, // 32 x i16 + v64i16 = 43, // 64 x i16 + v128i16 = 44, //128 x i16 + + v1i32 = 45, // 1 x i32 + v2i32 = 46, // 2 x i32 + v3i32 = 47, // 3 x i32 + v4i32 = 48, // 4 x i32 + v5i32 = 49, // 5 x i32 + v8i32 = 50, // 8 x i32 + v16i32 = 51, // 16 x i32 + v32i32 = 52, // 32 x i32 + v64i32 = 53, // 64 x i32 + v128i32 = 54, // 128 x i32 + v256i32 = 55, // 256 x i32 + v512i32 = 56, // 512 x i32 + v1024i32 = 57, // 1024 x i32 + v2048i32 = 58, // 2048 x i32 + + v1i64 = 59, // 1 x i64 + v2i64 = 60, // 2 x i64 + v4i64 = 61, // 4 x i64 + v8i64 = 62, // 8 x i64 + v16i64 = 63, // 16 x i64 + v32i64 = 64, // 32 x i64 + + v1i128 = 65, // 1 x i128 FIRST_INTEGER_FIXEDLEN_VECTOR_VALUETYPE = v1i1, LAST_INTEGER_FIXEDLEN_VECTOR_VALUETYPE = v1i128, - v2f16 = 65, // 2 x f16 - v3f16 = 66, // 3 x f16 - v4f16 = 67, // 4 x f16 - v8f16 = 68, // 8 x f16 - v16f16 = 69, // 16 x f16 - v32f16 = 70, // 32 x f16 - v64f16 = 71, // 64 x f16 - v128f16 = 72, // 128 x f16 - v2bf16 = 73, // 2 x bf16 - v3bf16 = 74, // 3 x bf16 - v4bf16 = 75, // 4 x bf16 - v8bf16 = 76, // 8 x bf16 - v16bf16 = 77, // 16 x bf16 - v32bf16 = 78, // 32 x bf16 - v64bf16 = 79, // 64 x bf16 - v128bf16 = 80, // 128 x bf16 - v1f32 = 81, // 1 x f32 - v2f32 = 82, // 2 x f32 - v3f32 = 83, // 3 x f32 - v4f32 = 84, // 4 x f32 - v5f32 = 85, // 5 x f32 - v8f32 = 86, // 8 x f32 - v16f32 = 87, // 16 x f32 - v32f32 = 88, // 32 x f32 - v64f32 = 89, // 64 x f32 - v128f32 = 90, // 128 x f32 - v256f32 = 91, // 256 x f32 - v512f32 = 92, // 512 x f32 - v1024f32 = 93, // 1024 x f32 - v2048f32 = 94, // 2048 x f32 - v1f64 = 95, // 1 x f64 - v2f64 = 96, // 2 x f64 - v4f64 = 97, // 4 x f64 - v8f64 = 98, // 8 x f64 - v16f64 = 99, // 16 x f64 - v32f64 = 100, // 32 x f64 + v2f16 = 66, // 2 x f16 + v3f16 = 67, // 3 x f16 + v4f16 = 68, // 4 x f16 + v8f16 = 69, // 8 x f16 + v16f16 = 70, // 16 x f16 + v32f16 = 71, // 32 x f16 + v64f16 = 72, // 64 x f16 + v128f16 = 73, // 128 x f16 + v2bf16 = 74, // 2 x bf16 + v3bf16 = 75, // 3 x bf16 + v4bf16 = 76, // 4 x bf16 + v8bf16 = 77, // 8 x bf16 + v16bf16 = 78, // 16 x bf16 + v32bf16 = 79, // 32 x bf16 + v64bf16 = 80, // 64 x bf16 + v128bf16 = 81, // 128 x bf16 + v1f32 = 82, // 1 x f32 + v2f32 = 83, // 2 x f32 + v3f32 = 84, // 3 x f32 + v4f32 = 85, // 4 x f32 + v5f32 = 86, // 5 x f32 + v8f32 = 87, // 8 x f32 + v16f32 = 88, // 16 x f32 + v32f32 = 89, // 32 x f32 + v64f32 = 90, // 64 x f32 + v128f32 = 91, // 128 x f32 + v256f32 = 92, // 256 x f32 + v512f32 = 93, // 512 x f32 + v1024f32 = 94, // 1024 x f32 + v2048f32 = 95, // 2048 x f32 + v1f64 = 96, // 1 x f64 + v2f64 = 97, // 2 x f64 + v4f64 = 98, // 4 x f64 + v8f64 = 99, // 8 x f64 + v16f64 = 100, // 16 x f64 + v32f64 = 101, // 32 x f64 FIRST_FP_FIXEDLEN_VECTOR_VALUETYPE = v2f16, LAST_FP_FIXEDLEN_VECTOR_VALUETYPE = v32f64, @@ -160,59 +161,59 @@ namespace llvm { FIRST_FIXEDLEN_VECTOR_VALUETYPE = v1i1, LAST_FIXEDLEN_VECTOR_VALUETYPE = v32f64, - nxv1i1 = 101, // n x 1 x i1 - nxv2i1 = 102, // n x 2 x i1 - nxv4i1 = 103, // n x 4 x i1 - nxv8i1 = 104, // n x 8 x i1 - nxv16i1 = 105, // n x 16 x i1 - nxv32i1 = 106, // n x 32 x i1 - - nxv1i8 = 107, // n x 1 x i8 - nxv2i8 = 108, // n x 2 x i8 - nxv4i8 = 109, // n x 4 x i8 - nxv8i8 = 110, // n x 8 x i8 - nxv16i8 = 111, // n x 16 x i8 - nxv32i8 = 112, // n x 32 x i8 - - nxv1i16 = 113, // n x 1 x i16 - nxv2i16 = 114, // n x 2 x i16 - nxv4i16 = 115, // n x 4 x i16 - nxv8i16 = 116, // n x 8 x i16 - nxv16i16 = 117, // n x 16 x i16 - nxv32i16 = 118, // n x 32 x i16 - - nxv1i32 = 119, // n x 1 x i32 - nxv2i32 = 120, // n x 2 x i32 - nxv4i32 = 121, // n x 4 x i32 - nxv8i32 = 122, // n x 8 x i32 - nxv16i32 = 123, // n x 16 x i32 - nxv32i32 = 124, // n x 32 x i32 - - nxv1i64 = 125, // n x 1 x i64 - nxv2i64 = 126, // n x 2 x i64 - nxv4i64 = 127, // n x 4 x i64 - nxv8i64 = 128, // n x 8 x i64 - nxv16i64 = 129, // n x 16 x i64 - nxv32i64 = 130, // n x 32 x i64 + nxv1i1 = 102, // n x 1 x i1 + nxv2i1 = 103, // n x 2 x i1 + nxv4i1 = 104, // n x 4 x i1 + nxv8i1 = 105, // n x 8 x i1 + nxv16i1 = 106, // n x 16 x i1 + nxv32i1 = 107, // n x 32 x i1 + + nxv1i8 = 108, // n x 1 x i8 + nxv2i8 = 109, // n x 2 x i8 + nxv4i8 = 110, // n x 4 x i8 + nxv8i8 = 111, // n x 8 x i8 + nxv16i8 = 112, // n x 16 x i8 + nxv32i8 = 113, // n x 32 x i8 + + nxv1i16 = 114, // n x 1 x i16 + nxv2i16 = 115, // n x 2 x i16 + nxv4i16 = 116, // n x 4 x i16 + nxv8i16 = 117, // n x 8 x i16 + nxv16i16 = 118, // n x 16 x i16 + nxv32i16 = 119, // n x 32 x i16 + + nxv1i32 = 120, // n x 1 x i32 + nxv2i32 = 121, // n x 2 x i32 + nxv4i32 = 122, // n x 4 x i32 + nxv8i32 = 123, // n x 8 x i32 + nxv16i32 = 124, // n x 16 x i32 + nxv32i32 = 125, // n x 32 x i32 + + nxv1i64 = 126, // n x 1 x i64 + nxv2i64 = 127, // n x 2 x i64 + nxv4i64 = 128, // n x 4 x i64 + nxv8i64 = 129, // n x 8 x i64 + nxv16i64 = 130, // n x 16 x i64 + nxv32i64 = 131, // n x 32 x i64 FIRST_INTEGER_SCALABLE_VECTOR_VALUETYPE = nxv1i1, LAST_INTEGER_SCALABLE_VECTOR_VALUETYPE = nxv32i64, - nxv2f16 = 131, // n x 2 x f16 - nxv4f16 = 132, // n x 4 x f16 - nxv8f16 = 133, // n x 8 x f16 - nxv2bf16 = 134, // n x 2 x bf16 - nxv4bf16 = 135, // n x 4 x bf16 - nxv8bf16 = 136, // n x 8 x bf16 - nxv1f32 = 137, // n x 1 x f32 - nxv2f32 = 138, // n x 2 x f32 - nxv4f32 = 139, // n x 4 x f32 - nxv8f32 = 140, // n x 8 x f32 - nxv16f32 = 141, // n x 16 x f32 - nxv1f64 = 142, // n x 1 x f64 - nxv2f64 = 143, // n x 2 x f64 - nxv4f64 = 144, // n x 4 x f64 - nxv8f64 = 145, // n x 8 x f64 + nxv2f16 = 132, // n x 2 x f16 + nxv4f16 = 133, // n x 4 x f16 + nxv8f16 = 134, // n x 8 x f16 + nxv2bf16 = 135, // n x 2 x bf16 + nxv4bf16 = 136, // n x 4 x bf16 + nxv8bf16 = 137, // n x 8 x bf16 + nxv1f32 = 138, // n x 1 x f32 + nxv2f32 = 139, // n x 2 x f32 + nxv4f32 = 140, // n x 4 x f32 + nxv8f32 = 141, // n x 8 x f32 + nxv16f32 = 142, // n x 16 x f32 + nxv1f64 = 143, // n x 1 x f64 + nxv2f64 = 144, // n x 2 x f64 + nxv4f64 = 145, // n x 4 x f64 + nxv8f64 = 146, // n x 8 x f64 FIRST_FP_SCALABLE_VECTOR_VALUETYPE = nxv2f16, LAST_FP_SCALABLE_VECTOR_VALUETYPE = nxv8f64, @@ -223,20 +224,20 @@ namespace llvm { FIRST_VECTOR_VALUETYPE = v1i1, LAST_VECTOR_VALUETYPE = nxv8f64, - x86mmx = 146, // This is an X86 MMX value + x86mmx = 147, // This is an X86 MMX value - Glue = 147, // This glues nodes together during pre-RA sched + Glue = 148, // This glues nodes together during pre-RA sched - isVoid = 148, // This has no value + isVoid = 149, // This has no value - Untyped = 149, // This value takes a register, but has - // unspecified type. The register class - // will be determined by the opcode. + Untyped = 150, // This value takes a register, but has + // unspecified type. The register class + // will be determined by the opcode. - exnref = 150, // WebAssembly's exnref type + exnref = 151, // WebAssembly's exnref type - FIRST_VALUETYPE = 1, // This is always the beginning of the list. - LAST_VALUETYPE = 151, // This always remains at the end of the list. + FIRST_VALUETYPE = 1, // This is always the beginning of the list. + LAST_VALUETYPE = 152, // This always remains at the end of the list. // This is the current maximum for LAST_VALUETYPE. // MVT::MAX_ALLOWED_VALUETYPE is used for asserts and to size bit vectors @@ -423,6 +424,12 @@ namespace llvm { return getVectorVT(EltVT, EltCnt / 2); } + /// Return true if the width is a power of 2. + bool isPow2Size() { + unsigned BitWidth = getSizeInBits(); + return !(BitWidth & (BitWidth - 1)); + } + /// Returns true if the given vector is a power of 2. bool isPow2VectorType() const { unsigned NElts = getVectorNumElements(); @@ -775,6 +782,7 @@ namespace llvm { case nxv16i1: case nxv2i8: case nxv1i16: return TypeSize::Scalable(16); + case i24: return TypeSize::Fixed(24); case f32 : case i32 : case v32i1: @@ -934,6 +942,18 @@ namespace llvm { return getSizeInBits().isByteSized(); } + /// getNumParts - Return the number of parts with PartBits bits that make up + /// this VT. + unsigned getNumParts(unsigned PartBits) const { + return divideCeil(getSizeInBits(), PartBits); + } + + /// getNumParts - Return the number of parts of type PartVT that make up + /// this VT. + unsigned getNumParts(MVT PartVT) const { + return getNumParts(PartVT.getSizeInBits()); + } + /// Return true if this has more bits than VT. bool bitsGT(MVT VT) const { return getSizeInBits() > VT.getSizeInBits(); @@ -981,6 +1001,8 @@ namespace llvm { return MVT::i8; case 16: return MVT::i16; + case 24: + return MVT::i24; case 32: return MVT::i32; case 64: diff --git a/llvm/lib/CodeGen/TargetLoweringBase.cpp b/llvm/lib/CodeGen/TargetLoweringBase.cpp index c8220928af935b..6a41476da09c5a 100644 --- a/llvm/lib/CodeGen/TargetLoweringBase.cpp +++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp @@ -728,6 +728,20 @@ void TargetLoweringBase::initActions() { setOperationAction(ISD::VECREDUCE_UMIN, VT, Expand); setOperationAction(ISD::VECREDUCE_FMAX, VT, Expand); setOperationAction(ISD::VECREDUCE_FMIN, VT, Expand); + + // For most targets, non power of 2 types default to expand. + if (VT.isScalarInteger() && !VT.isPow2Size()) { + std::fill(std::begin(OpActions[(unsigned)VT.SimpleTy]), + std::end(OpActions[(unsigned)VT.SimpleTy]), Expand); + for (MVT OtherVT : MVT::all_valuetypes()) { + for (auto VTs : {std::make_pair(VT, OtherVT), + std::make_pair(OtherVT, VT)}) { + for (unsigned ExtType : {ISD::EXTLOAD, ISD::ZEXTLOAD, ISD::SEXTLOAD}) + setLoadExtAction(ExtType, VTs.first, VTs.second, Expand); + setTruncStoreAction(VTs.first, VTs.second, Expand); + } + } + } } // Most targets ignore the @llvm.prefetch intrinsic. @@ -816,10 +830,8 @@ TargetLoweringBase::getTypeConversion(LLVMContext &Context, EVT VT) const { MVT NVT = TransformToType[SVT.SimpleTy]; LegalizeTypeAction LA = ValueTypeActions.getTypeAction(SVT); - assert((LA == TypeLegal || LA == TypeSoftenFloat || - LA == TypeSoftPromoteHalf || - (NVT.isVector() || - ValueTypeActions.getTypeAction(NVT) != TypePromoteInteger)) && + assert(((LA != TypePromoteInteger && LA != TypeExpandInteger) || + ValueTypeActions.getTypeAction(NVT) != TypePromoteInteger) && "Promote may not follow Expand or Promote"); if (LA == TypeSplitVector) @@ -1171,23 +1183,33 @@ void TargetLoweringBase::computeRegisterProperties( unsigned LargestIntReg = MVT::LAST_INTEGER_VALUETYPE; for (; RegClassForVT[LargestIntReg] == nullptr; --LargestIntReg) assert(LargestIntReg != MVT::i1 && "No integer registers defined!"); + MVT LargestIntVT = (MVT::SimpleValueType)LargestIntReg; // Every integer value type larger than this largest register takes twice as // many registers to represent as the previous ValueType. - for (unsigned ExpandedReg = LargestIntReg + 1; + for (unsigned HalfReg = LargestIntReg, ExpandedReg = LargestIntReg + 1; ExpandedReg <= MVT::LAST_INTEGER_VALUETYPE; ++ExpandedReg) { - NumRegistersForVT[ExpandedReg] = 2*NumRegistersForVT[ExpandedReg-1]; - RegisterTypeForVT[ExpandedReg] = (MVT::SimpleValueType)LargestIntReg; - TransformToType[ExpandedReg] = (MVT::SimpleValueType)(ExpandedReg - 1); - ValueTypeActions.setTypeAction((MVT::SimpleValueType)ExpandedReg, - TypeExpandInteger); + MVT ExpandedVT = (MVT::SimpleValueType)ExpandedReg; + NumRegistersForVT[ExpandedReg] = ExpandedVT.getNumParts(LargestIntVT); + RegisterTypeForVT[ExpandedReg] = LargestIntVT; + if (ExpandedVT.isPow2Size()) { + TransformToType[ExpandedReg] = (MVT::SimpleValueType)HalfReg; + ValueTypeActions.setTypeAction(ExpandedVT, TypeExpandInteger); + HalfReg = ExpandedReg; + } else { + assert(ExpandedReg < MVT::LAST_INTEGER_VALUETYPE && + "Expected a pow 2 type larger than any non pow 2 type"); + TransformToType[ExpandedReg] = (MVT::SimpleValueType)(ExpandedReg + 1); + ValueTypeActions.setTypeAction(ExpandedVT, TypePromoteInteger); + } } + // Inspect all of the ValueType's smaller than the largest integer // register to see which ones need promotion. unsigned LegalIntReg = LargestIntReg; - for (unsigned IntReg = LargestIntReg - 1; - IntReg >= (unsigned)MVT::i1; --IntReg) { + for (unsigned IntReg = LargestIntReg - 1; IntReg >= (unsigned)MVT::i1; + --IntReg) { MVT IVT = (MVT::SimpleValueType)IntReg; if (isTypeLegal(IVT)) { LegalIntReg = IntReg; @@ -1344,6 +1366,7 @@ void TargetLoweringBase::computeRegisterProperties( if (NVT == VT) { // Type is already a power of 2. The default action is to split. TransformToType[i] = MVT::Other; + ValueTypeActions.setTypeAction(VT, PreferredAction); if (PreferredAction == TypeScalarizeVector) ValueTypeActions.setTypeAction(VT, TypeScalarizeVector); else if (PreferredAction == TypeSplitVector) diff --git a/llvm/lib/CodeGen/ValueTypes.cpp b/llvm/lib/CodeGen/ValueTypes.cpp index 68e8acd218ba3f..b9c0d609e7edc1 100644 --- a/llvm/lib/CodeGen/ValueTypes.cpp +++ b/llvm/lib/CodeGen/ValueTypes.cpp @@ -176,6 +176,7 @@ Type *EVT::getTypeForEVT(LLVMContext &Context) const { case MVT::i1: return Type::getInt1Ty(Context); case MVT::i8: return Type::getInt8Ty(Context); case MVT::i16: return Type::getInt16Ty(Context); + case MVT::i24: return IntegerType::get(Context, 24); case MVT::i32: return Type::getInt32Ty(Context); case MVT::i64: return Type::getInt64Ty(Context); case MVT::i128: return IntegerType::get(Context, 128); diff --git a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp index 16b811185233fa..5cc068bcd64743 100644 --- a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp +++ b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp @@ -134,7 +134,7 @@ AMDGPUTargetLowering::AMDGPUTargetLowering(const TargetMachine &TM, } for (MVT VT : MVT::integer_valuetypes()) { - if (VT == MVT::i64) + if (!VT.isPow2Size() || VT.bitsGE(MVT::i64)) continue; setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i1, Promote); diff --git a/llvm/lib/Target/AMDGPU/R600ISelLowering.cpp b/llvm/lib/Target/AMDGPU/R600ISelLowering.cpp index e0c63bc5e8e182..03aeb744582cdc 100644 --- a/llvm/lib/Target/AMDGPU/R600ISelLowering.cpp +++ b/llvm/lib/Target/AMDGPU/R600ISelLowering.cpp @@ -76,6 +76,9 @@ R600TargetLowering::R600TargetLowering(const TargetMachine &TM, // EXTLOAD should be the same as ZEXTLOAD. It is legal for some address // spaces, so it is custom lowered to handle those where it isn't. for (MVT VT : MVT::integer_valuetypes()) { + if (!VT.isPow2Size()) + continue; + setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i1, Promote); setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i8, Custom); setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i16, Custom); diff --git a/llvm/lib/Target/AVR/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp index bf9b32e1278e38..f65e7273b9d405 100644 --- a/llvm/lib/Target/AVR/AVRISelLowering.cpp +++ b/llvm/lib/Target/AVR/AVRISelLowering.cpp @@ -66,6 +66,9 @@ AVRTargetLowering::AVRTargetLowering(const AVRTargetMachine &TM, setTruncStoreAction(MVT::i16, MVT::i8, Expand); for (MVT VT : MVT::integer_valuetypes()) { + if (!VT.isPow2Size()) + continue; + setOperationAction(ISD::ADDC, VT, Legal); setOperationAction(ISD::SUBC, VT, Legal); setOperationAction(ISD::ADDE, VT, Legal); diff --git a/llvm/lib/Target/X86/X86FastISel.cpp b/llvm/lib/Target/X86/X86FastISel.cpp index 23512f3f85dd81..2bb08f73477e96 100644 --- a/llvm/lib/Target/X86/X86FastISel.cpp +++ b/llvm/lib/Target/X86/X86FastISel.cpp @@ -2915,9 +2915,11 @@ bool X86FastISel::fastLowerIntrinsicCall(const IntrinsicInst *II) { unsigned ResultReg = 0; // Check if we have an immediate version. if (const auto *CI = dyn_cast(RHS)) { - static const uint16_t Opc[2][4] = { - { X86::INC8r, X86::INC16r, X86::INC32r, X86::INC64r }, - { X86::DEC8r, X86::DEC16r, X86::DEC32r, X86::DEC64r } + static const uint16_t Opc[2][5] = { + { X86::INC8r, X86::INC16r, X86::INSTRUCTION_LIST_END, + X86::INC32r, X86::INC64r }, + { X86::DEC8r, X86::DEC16r, X86::INSTRUCTION_LIST_END, + X86::DEC32r, X86::DEC64r } }; if (CI->isOne() && (BaseOpc == ISD::ADD || BaseOpc == ISD::SUB) && @@ -2948,8 +2950,10 @@ bool X86FastISel::fastLowerIntrinsicCall(const IntrinsicInst *II) { // it manually. if (BaseOpc == X86ISD::UMUL && !ResultReg) { static const uint16_t MULOpc[] = - { X86::MUL8r, X86::MUL16r, X86::MUL32r, X86::MUL64r }; - static const MCPhysReg Reg[] = { X86::AL, X86::AX, X86::EAX, X86::RAX }; + { X86::MUL8r, X86::MUL16r, X86::INSTRUCTION_LIST_END, + X86::MUL32r, X86::MUL64r }; + static const MCPhysReg Reg[] = { X86::AL, X86::AX, X86::NoRegister, + X86::EAX, X86::RAX }; // First copy the first operand into RAX, which is an implicit input to // the X86::MUL*r instruction. BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, @@ -2959,7 +2963,8 @@ bool X86FastISel::fastLowerIntrinsicCall(const IntrinsicInst *II) { TLI.getRegClassFor(VT), RHSReg, RHSIsKill); } else if (BaseOpc == X86ISD::SMUL && !ResultReg) { static const uint16_t MULOpc[] = - { X86::IMUL8r, X86::IMUL16rr, X86::IMUL32rr, X86::IMUL64rr }; + { X86::IMUL8r, X86::IMUL16rr, X86::INSTRUCTION_LIST_END, + X86::IMUL32rr, X86::IMUL64rr }; if (VT == MVT::i8) { // Copy the first operand into AL, which is an implicit input to the // X86::IMUL8r instruction. diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index c3e89854bc489b..013a0a064066a2 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -2285,7 +2285,7 @@ bool X86TargetLowering::isSafeMemOpType(MVT VT) const { return X86ScalarSSEf32; else if (VT == MVT::f64) return X86ScalarSSEf64; - return true; + return TargetLowering::isSafeMemOpType(VT); } bool X86TargetLowering::allowsMisalignedMemoryAccesses( diff --git a/llvm/utils/TableGen/CodeGenTarget.cpp b/llvm/utils/TableGen/CodeGenTarget.cpp index 6a4095b1cd2dbe..e9d7650c7c7910 100644 --- a/llvm/utils/TableGen/CodeGenTarget.cpp +++ b/llvm/utils/TableGen/CodeGenTarget.cpp @@ -61,6 +61,7 @@ StringRef llvm::getEnumName(MVT::SimpleValueType T) { case MVT::i1: return "MVT::i1"; case MVT::i8: return "MVT::i8"; case MVT::i16: return "MVT::i16"; + case MVT::i24: return "MVT::i24"; case MVT::i32: return "MVT::i32"; case MVT::i64: return "MVT::i64"; case MVT::i128: return "MVT::i128"; From 5fd5c88127254475918b9a2de2fbaff105aae979 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 20 Oct 2019 16:50:01 +0200 Subject: [PATCH 11/21] Add OMF object format. --- clang/lib/CodeGen/BackendUtil.cpp | 2 + clang/lib/CodeGen/CGObjCMac.cpp | 1 + clang/lib/CodeGen/CodeGenModule.cpp | 2 + .../GDBRemoteCommunicationClient.cpp | 1 + llvm/include/llvm/ADT/Triple.h | 1 + llvm/include/llvm/BinaryFormat/Magic.h | 2 + .../CodeGen/TargetLoweringObjectFileImpl.h | 11 +++++ llvm/include/llvm/MC/MCAsmInfoOMF.h | 24 ++++++++++ llvm/include/llvm/MC/MCContext.h | 16 +++++++ llvm/include/llvm/MC/MCOMFStreamer.h | 46 ++++++++++++++++++ llvm/include/llvm/MC/MCObjectFileInfo.h | 3 +- llvm/include/llvm/MC/MCSection.h | 3 +- llvm/include/llvm/MC/MCSectionOMF.h | 46 ++++++++++++++++++ llvm/include/llvm/MC/MCSymbol.h | 3 ++ llvm/include/llvm/MC/MCSymbolOMF.h | 34 +++++++++++++ llvm/include/llvm/Support/TargetRegistry.h | 9 ++++ llvm/lib/BinaryFormat/Magic.cpp | 7 +++ llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 6 ++- .../CodeGen/TargetLoweringObjectFileImpl.cpp | 28 +++++++++++ llvm/lib/MC/CMakeLists.txt | 3 ++ llvm/lib/MC/MCAsmInfoOMF.cpp | 23 +++++++++ llvm/lib/MC/MCContext.cpp | 27 +++++++++++ llvm/lib/MC/MCOMFStreamer.cpp | 48 +++++++++++++++++++ llvm/lib/MC/MCObjectFileInfo.cpp | 13 +++++ llvm/lib/MC/MCParser/AsmParser.cpp | 4 ++ llvm/lib/MC/MCSectionOMF.cpp | 27 +++++++++++ llvm/lib/Object/Binary.cpp | 3 ++ llvm/lib/Object/ObjectFile.cpp | 3 +- llvm/lib/Object/SymbolicFile.cpp | 2 + llvm/lib/Support/Triple.cpp | 1 + .../lib/Target/ARM/AsmParser/ARMAsmParser.cpp | 1 + .../Instrumentation/AddressSanitizer.cpp | 1 + 32 files changed, 396 insertions(+), 5 deletions(-) create mode 100644 llvm/include/llvm/MC/MCAsmInfoOMF.h create mode 100644 llvm/include/llvm/MC/MCOMFStreamer.h create mode 100644 llvm/include/llvm/MC/MCSectionOMF.h create mode 100644 llvm/include/llvm/MC/MCSymbolOMF.h create mode 100644 llvm/lib/MC/MCAsmInfoOMF.cpp create mode 100644 llvm/lib/MC/MCOMFStreamer.cpp create mode 100644 llvm/lib/MC/MCSectionOMF.cpp diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 9e6d5e4593d3d6..b1f22bfde681cb 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -257,6 +257,8 @@ static bool asanUseGlobalsGC(const Triple &T, const CodeGenOptions &CGOpts) { return CGOpts.DataSections && !CGOpts.DisableIntegratedAS; case Triple::XCOFF: llvm::report_fatal_error("ASan not implemented for XCOFF."); + case Triple::OMF: + llvm::report_fatal_error("ASan not implemented for OMF."); case Triple::Wasm: case Triple::UnknownObjectFormat: break; diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 1d0379afb4b59e..352b7288fb5bed 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -5107,6 +5107,7 @@ std::string CGObjCCommonMac::GetSectionName(StringRef Section, return ("." + Section.substr(2) + "$B").str(); case llvm::Triple::Wasm: case llvm::Triple::XCOFF: + case llvm::Triple::OMF: llvm::report_fatal_error( "Objective-C support is unimplemented for object file format."); } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 71778ac31660ef..e127e81218fdd1 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -4906,6 +4906,8 @@ CodeGenModule::GetAddrOfConstantCFString(const StringLiteral *Literal) { llvm_unreachable("unknown file format"); case llvm::Triple::XCOFF: llvm_unreachable("XCOFF is not yet implemented"); + case llvm::Triple::OMF: + llvm_unreachable("OMF is not yet implemented"); case llvm::Triple::COFF: case llvm::Triple::ELF: case llvm::Triple::Wasm: diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index c75d5e106cd02d..7c6b8abbc86a49 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -2122,6 +2122,7 @@ bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) { break; case llvm::Triple::Wasm: case llvm::Triple::XCOFF: + case llvm::Triple::OMF: LLDB_LOGF(log, "error: not supported target architecture"); return false; case llvm::Triple::UnknownObjectFormat: diff --git a/llvm/include/llvm/ADT/Triple.h b/llvm/include/llvm/ADT/Triple.h index 89679619dd55cd..d47e1b4f2c07d8 100644 --- a/llvm/include/llvm/ADT/Triple.h +++ b/llvm/include/llvm/ADT/Triple.h @@ -228,6 +228,7 @@ class Triple { COFF, ELF, MachO, + OMF, Wasm, XCOFF, }; diff --git a/llvm/include/llvm/BinaryFormat/Magic.h b/llvm/include/llvm/BinaryFormat/Magic.h index 78227ddbe0954d..d3aa2ea486db84 100644 --- a/llvm/include/llvm/BinaryFormat/Magic.h +++ b/llvm/include/llvm/BinaryFormat/Magic.h @@ -50,6 +50,8 @@ struct file_magic { wasm_object, ///< WebAssembly Object file pdb, ///< Windows PDB debug info file tapi_file, ///< Text-based Dynamic Library Stub file + omf_object, ///< IEEE 695 OMF assembler file + omf_archive, ///< IEEE 695 OMF LIBRARY file }; bool is_object() const { return V != unknown; } diff --git a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h index 6e2c0973e3547d..6b9ad583f66df0 100644 --- a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h +++ b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h @@ -267,6 +267,17 @@ class TargetLoweringObjectFileXCOFF : public TargetLoweringObjectFile { const TargetMachine &TM) const override; }; +class TargetLoweringObjectFileOMF : public TargetLoweringObjectFile { +public: + TargetLoweringObjectFileOMF() = default; + ~TargetLoweringObjectFileOMF() override = default; + + MCSection *getExplicitSectionGlobal(const GlobalObject *GO, SectionKind Kind, + const TargetMachine &TM) const override; + MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind, + const TargetMachine &TM) const override; +}; + } // end namespace llvm #endif // LLVM_CODEGEN_TARGETLOWERINGOBJECTFILEIMPL_H diff --git a/llvm/include/llvm/MC/MCAsmInfoOMF.h b/llvm/include/llvm/MC/MCAsmInfoOMF.h new file mode 100644 index 00000000000000..2f1c8c2b2b9f2d --- /dev/null +++ b/llvm/include/llvm/MC/MCAsmInfoOMF.h @@ -0,0 +1,24 @@ +//===-- llvm/MC/MCAsmInfoOMF.h - OMF Asm info -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_MC_MCASMINFOWASM_H +#define LLVM_MC_MCASMINFOWASM_H + +#include "llvm/MC/MCAsmInfo.h" + +namespace llvm { +class MCAsmInfoOMF : public MCAsmInfo { + virtual void anchor(); + +protected: + MCAsmInfoOMF(); +}; +} + +#endif // LLVM_MC_MCASMINFOWASM_H diff --git a/llvm/include/llvm/MC/MCContext.h b/llvm/include/llvm/MC/MCContext.h index 3f106c697b01c2..02a3b2b10aacda 100644 --- a/llvm/include/llvm/MC/MCContext.h +++ b/llvm/include/llvm/MC/MCContext.h @@ -51,6 +51,7 @@ namespace llvm { class MCSectionCOFF; class MCSectionELF; class MCSectionMachO; + class MCSectionOMF; class MCSectionWasm; class MCSectionXCOFF; class MCStreamer; @@ -94,6 +95,7 @@ namespace llvm { SpecificBumpPtrAllocator COFFAllocator; SpecificBumpPtrAllocator ELFAllocator; SpecificBumpPtrAllocator MachOAllocator; + SpecificBumpPtrAllocator OMFAllocator; SpecificBumpPtrAllocator WasmAllocator; SpecificBumpPtrAllocator XCOFFAllocator; @@ -277,11 +279,23 @@ namespace llvm { } }; + struct OMFSectionKey { + std::string SectionName; + + explicit OMFSectionKey(StringRef SectionName) + : SectionName(SectionName) {} + + bool operator<(const OMFSectionKey &Other) const { + return SectionName < Other.SectionName; + } + }; + StringMap MachOUniquingMap; std::map ELFUniquingMap; std::map COFFUniquingMap; std::map WasmUniquingMap; std::map XCOFFUniquingMap; + std::map OMFUniquingMap; StringMap RelSecNames; SpecificBumpPtrAllocator MCSubtargetAllocator; @@ -559,6 +573,8 @@ namespace llvm { SectionKind K, const char *BeginSymName = nullptr); + MCSectionOMF *getOMFSection(const Twine &Section, SectionKind Kind); + // Create and save a copy of STI and return a reference to the copy. MCSubtargetInfo &getSubtargetCopy(const MCSubtargetInfo &STI); diff --git a/llvm/include/llvm/MC/MCOMFStreamer.h b/llvm/include/llvm/MC/MCOMFStreamer.h new file mode 100644 index 00000000000000..1868c6b71ffd93 --- /dev/null +++ b/llvm/include/llvm/MC/MCOMFStreamer.h @@ -0,0 +1,46 @@ +//===- MCOMFStreamer.h - MCStreamer OMF Object File Interface ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_MC_MCOMFSTREAMER_H +#define LLVM_MC_MCOMFSTREAMER_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCDirectives.h" +#include "llvm/MC/MCObjectStreamer.h" +#include "llvm/MC/MCObjectWriter.h" + +namespace llvm { + +class MCAsmBackend; +class MCCodeEmitter; +class MCExpr; +class MCInst; + +class MCOMFStreamer : public MCObjectStreamer { +public: + MCOMFStreamer(MCContext &Context, std::unique_ptr TAB, + std::unique_ptr OW, + std::unique_ptr Emitter) + : MCObjectStreamer(Context, std::move(TAB), std::move(OW), + std::move(Emitter)) {} + + ~MCOMFStreamer() override = default; + + bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override; + void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, + unsigned ByteAlignment) override; + void emitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr, + uint64_t Size = 0, unsigned ByteAlignment = 0, + SMLoc Loc = SMLoc()) override; + void emitInstToData(const MCInst &Inst, const MCSubtargetInfo &) override; +}; + +} // end namespace llvm + +#endif // LLVM_MC_MCOMFSTREAMER_H diff --git a/llvm/include/llvm/MC/MCObjectFileInfo.h b/llvm/include/llvm/MC/MCObjectFileInfo.h index ca04d8e8d3b689..beb3af9eab65d6 100644 --- a/llvm/include/llvm/MC/MCObjectFileInfo.h +++ b/llvm/include/llvm/MC/MCObjectFileInfo.h @@ -405,7 +405,7 @@ class MCObjectFileInfo { return EHFrameSection; } - enum Environment { IsMachO, IsELF, IsCOFF, IsWasm, IsXCOFF }; + enum Environment { IsMachO, IsELF, IsCOFF, IsWasm, IsXCOFF, IsOMF }; Environment getObjectFileType() const { return Env; } bool isPositionIndependent() const { return PositionIndependent; } @@ -422,6 +422,7 @@ class MCObjectFileInfo { void initCOFFMCObjectFileInfo(const Triple &T); void initWasmMCObjectFileInfo(const Triple &T); void initXCOFFMCObjectFileInfo(const Triple &T); + void initOMFMCObjectFileInfo(const Triple &T); MCSection *getDwarfComdatSection(const char *Name, uint64_t Hash) const; public: diff --git a/llvm/include/llvm/MC/MCSection.h b/llvm/include/llvm/MC/MCSection.h index a68e06e661beab..1b2a08c4172e20 100644 --- a/llvm/include/llvm/MC/MCSection.h +++ b/llvm/include/llvm/MC/MCSection.h @@ -40,7 +40,8 @@ class MCSection { public: static constexpr unsigned NonUniqueID = ~0U; - enum SectionVariant { SV_COFF = 0, SV_ELF, SV_MachO, SV_Wasm, SV_XCOFF }; + enum SectionVariant { SV_COFF = 0, SV_ELF, SV_MachO, SV_OMF, SV_Wasm, + SV_XCOFF }; /// Express the state of bundle locked groups while emitting code. enum BundleLockStateType { diff --git a/llvm/include/llvm/MC/MCSectionOMF.h b/llvm/include/llvm/MC/MCSectionOMF.h new file mode 100644 index 00000000000000..2c486d7174bd72 --- /dev/null +++ b/llvm/include/llvm/MC/MCSectionOMF.h @@ -0,0 +1,46 @@ +//===- MCSectionOMF.h - OMF Machine Code Sections ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the MCSectionWasm class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_MC_MCSECTIONOMF_H +#define LLVM_MC_MCSECTIONOMF_H + +#include "llvm/MC/MCSection.h" + +namespace llvm { + +class MCSectionOMF final : public MCSection { + SmallString<8> SectionName; + +private: + friend class MCContext; + MCSectionOMF(StringRef Name, SectionKind K, MCSymbol *Begin); + +public: + ~MCSectionOMF(); + + StringRef getSectionName() const { return SectionName; } + + void PrintSwitchToSection(const MCAsmInfo &MAI, const Triple &T, + raw_ostream &OS, + const MCExpr *Subsection) const override; + bool UseCodeAlign() const override { return getKind().isText(); } + bool isVirtualSection() const override { return getKind().isBSS(); } + + static bool classof(const MCSection *S) { + return S->getVariant() == SV_OMF; + } +}; + +} // end namespace llvm + +#endif diff --git a/llvm/include/llvm/MC/MCSymbol.h b/llvm/include/llvm/MC/MCSymbol.h index 84263bf94035eb..6184c22870f618 100644 --- a/llvm/include/llvm/MC/MCSymbol.h +++ b/llvm/include/llvm/MC/MCSymbol.h @@ -47,6 +47,7 @@ class MCSymbol { SymbolKindCOFF, SymbolKindELF, SymbolKindMachO, + SymbolKindOMF, SymbolKindWasm, SymbolKindXCOFF, }; @@ -281,6 +282,8 @@ class MCSymbol { bool isXCOFF() const { return Kind == SymbolKindXCOFF; } + bool isOMF() const { return Kind == SymbolKindOMF; } + /// @} /// \name Variable Symbols /// @{ diff --git a/llvm/include/llvm/MC/MCSymbolOMF.h b/llvm/include/llvm/MC/MCSymbolOMF.h new file mode 100644 index 00000000000000..8c891d6c0cbc18 --- /dev/null +++ b/llvm/include/llvm/MC/MCSymbolOMF.h @@ -0,0 +1,34 @@ +//===- MCSymbolOMF.h - -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_MC_MCSYMBOLOMF_H +#define LLVM_MC_MCSYMBOLOMF_H + +#include "llvm/MC/MCSymbol.h" + +namespace llvm { +class MCSymbolOMF : public MCSymbol { + /// An expression describing how to calculate the size of a symbol. If a + /// symbol has no size this field will be NULL. + const MCExpr *SymbolSize = nullptr; + +public: + MCSymbolOMF(const StringMapEntry *Name, bool isTemporary) + : MCSymbol(SymbolKindOMF, Name, isTemporary) {} + void setSize(const MCExpr *SS) { SymbolSize = SS; } + + const MCExpr *getSize() const { return SymbolSize; } + + static bool classof(const MCSymbol *S) { return S->isOMF(); } + +private: + void setIsBindingSet() const; +}; +} + +#endif diff --git a/llvm/include/llvm/Support/TargetRegistry.h b/llvm/include/llvm/Support/TargetRegistry.h index d91eabae823579..86a1f3476806a9 100644 --- a/llvm/include/llvm/Support/TargetRegistry.h +++ b/llvm/include/llvm/Support/TargetRegistry.h @@ -95,6 +95,11 @@ MCStreamer *createMachOStreamer(MCContext &Ctx, std::unique_ptr &&CE, bool RelaxAll, bool DWARFMustBeAtTheEnd, bool LabelSections = false); +MCStreamer *createOMFStreamer(MCContext &Ctx, + std::unique_ptr &&TAB, + std::unique_ptr &&OW, + std::unique_ptr &&CE, + bool RelaxAll); MCStreamer *createWasmStreamer(MCContext &Ctx, std::unique_ptr &&TAB, std::unique_ptr &&OW, @@ -514,6 +519,10 @@ class Target { S = createXCOFFStreamer(Ctx, std::move(TAB), std::move(OW), std::move(Emitter), RelaxAll); break; + case Triple::OMF: + S = createOMFStreamer(Ctx, std::move(TAB), std::move(OW), + std::move(Emitter), RelaxAll); + break; } if (ObjectTargetStreamerCtorFn) ObjectTargetStreamerCtorFn(*S, STI); diff --git a/llvm/lib/BinaryFormat/Magic.cpp b/llvm/lib/BinaryFormat/Magic.cpp index 61b1504e59b0af..b5b0eab02ad7f2 100644 --- a/llvm/lib/BinaryFormat/Magic.cpp +++ b/llvm/lib/BinaryFormat/Magic.cpp @@ -216,6 +216,13 @@ file_magic llvm::identify_magic(StringRef Magic) { return file_magic::tapi_file; break; + case 0xE0: // IEEE 695 OMF format + if (startswith(Magic, "\xE0\11assembler")) + return file_magic::omf_object; + if (startswith(Magic, "\xE0\7LIBRARY")) + return file_magic::omf_archive; + break; + default: break; } diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index a23f39be6b2a35..81a4a13e078bc5 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -4730,7 +4730,8 @@ static const char *getSectionNameForBitcode(const Triple &T) { return ".llvmbc"; case Triple::XCOFF: llvm_unreachable("XCOFF is not yet implemented"); - break; + case Triple::OMF: + llvm_unreachable("OMF is not yet implemented"); } llvm_unreachable("Unimplemented ObjectFormatType"); } @@ -4746,7 +4747,8 @@ static const char *getSectionNameForCommandline(const Triple &T) { return ".llvmcmd"; case Triple::XCOFF: llvm_unreachable("XCOFF is not yet implemented"); - break; + case Triple::OMF: + llvm_unreachable("OMF is not yet implemented"); } llvm_unreachable("Unimplemented ObjectFormatType"); } diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp index 03254ed77d71c9..a3e47f69d3513f 100644 --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -2171,3 +2171,31 @@ MCSection *TargetLoweringObjectFileXCOFF::getSectionForTOCEntry( cast(Sym)->getUnqualifiedName(), XCOFF::XMC_TC, XCOFF::XTY_SD, XCOFF::C_HIDEXT, SectionKind::getData()); } + +//===----------------------------------------------------------------------===// +// OMF +//===----------------------------------------------------------------------===// + +MCSection *TargetLoweringObjectFileOMF::getExplicitSectionGlobal( + const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { + llvm_unreachable("getExplicitSectionGlobal not yet implemented"); + return nullptr; +} + +MCSection *TargetLoweringObjectFileOMF::SelectSectionForGlobal( + const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { + const MCObjectFileInfo *MOFI = getContext().getObjectFileInfo(); + if (Kind.isCommon()) + // ZDS doesn't support common. It just throws them in BSS and doesn't allow + // them to be redefined. RIP. + return MOFI->getBSSSection(); + if (Kind.isText()) + return MOFI->getTextSection(); + if (Kind.isData()) + return MOFI->getDataSection(); + if (Kind.isBSS()) + return MOFI->getBSSSection(); + if (Kind.isReadOnly()) + return MOFI->getReadOnlySection(); + return nullptr; +} diff --git a/llvm/lib/MC/CMakeLists.txt b/llvm/lib/MC/CMakeLists.txt index 9dca8d793395c7..1d7902180214b3 100644 --- a/llvm/lib/MC/CMakeLists.txt +++ b/llvm/lib/MC/CMakeLists.txt @@ -6,6 +6,7 @@ add_llvm_component_library(LLVMMC MCAsmInfoCOFF.cpp MCAsmInfoDarwin.cpp MCAsmInfoELF.cpp + MCAsmInfoOMF.cpp MCAsmInfoWasm.cpp MCAsmInfoXCOFF.cpp MCAsmMacro.cpp @@ -32,12 +33,14 @@ add_llvm_component_library(LLVMMC MCObjectFileInfo.cpp MCObjectStreamer.cpp MCObjectWriter.cpp + MCOMFStreamer.cpp MCRegisterInfo.cpp MCSchedule.cpp MCSection.cpp MCSectionCOFF.cpp MCSectionELF.cpp MCSectionMachO.cpp + MCSectionOMF.cpp MCSectionWasm.cpp MCSectionXCOFF.cpp MCStreamer.cpp diff --git a/llvm/lib/MC/MCAsmInfoOMF.cpp b/llvm/lib/MC/MCAsmInfoOMF.cpp new file mode 100644 index 00000000000000..74a55e2ee3fce2 --- /dev/null +++ b/llvm/lib/MC/MCAsmInfoOMF.cpp @@ -0,0 +1,23 @@ +//===- MCAsmInfoOMF.cpp - OMF asm properties ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines target asm properties related what form asm statements +// should take in general on OMF-based targets +// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/MCAsmInfoOMF.h" + +using namespace llvm; + +void MCAsmInfoOMF::anchor() {} + +MCAsmInfoOMF::MCAsmInfoOMF() { + PrivateLabelPrefix = "L"; +} diff --git a/llvm/lib/MC/MCContext.cpp b/llvm/lib/MC/MCContext.cpp index 92d99ec577b5a2..f1045bb1734780 100644 --- a/llvm/lib/MC/MCContext.cpp +++ b/llvm/lib/MC/MCContext.cpp @@ -26,6 +26,7 @@ #include "llvm/MC/MCSectionCOFF.h" #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCSectionOMF.h" #include "llvm/MC/MCSectionWasm.h" #include "llvm/MC/MCSectionXCOFF.h" #include "llvm/MC/MCStreamer.h" @@ -33,6 +34,7 @@ #include "llvm/MC/MCSymbolCOFF.h" #include "llvm/MC/MCSymbolELF.h" #include "llvm/MC/MCSymbolMachO.h" +#include "llvm/MC/MCSymbolOMF.h" #include "llvm/MC/MCSymbolWasm.h" #include "llvm/MC/MCSymbolXCOFF.h" #include "llvm/MC/SectionKind.h" @@ -179,6 +181,8 @@ MCSymbol *MCContext::createSymbolImpl(const StringMapEntry *Name, return new (Name, *this) MCSymbolELF(Name, IsTemporary); case MCObjectFileInfo::IsMachO: return new (Name, *this) MCSymbolMachO(Name, IsTemporary); + case MCObjectFileInfo::IsOMF: + return new (Name, *this) MCSymbolOMF(Name, IsTemporary); case MCObjectFileInfo::IsWasm: return new (Name, *this) MCSymbolWasm(Name, IsTemporary); case MCObjectFileInfo::IsXCOFF: @@ -631,6 +635,29 @@ MCSectionXCOFF *MCContext::getXCOFFSection(StringRef Section, return Result; } +MCSectionOMF *MCContext::getOMFSection(const Twine &Section, SectionKind Kind) { + auto IterBool = OMFUniquingMap.insert( + std::make_pair(OMFSectionKey{Section.str()}, nullptr)); + auto &Entry = *IterBool.first; + if (!IterBool.second) + return Entry.second; + + StringRef CachedName = Entry.first.SectionName; + + MCSymbol *Begin = createSymbol(CachedName, false, false); + + MCSectionOMF *Result = new (OMFAllocator.Allocate()) + MCSectionOMF(CachedName, Kind, Begin); + Entry.second = Result; + + auto *F = new MCDataFragment(); + Result->getFragmentList().insert(Result->begin(), F); + F->setParent(Result); + Begin->setFragment(F); + + return Result; +} + MCSubtargetInfo &MCContext::getSubtargetCopy(const MCSubtargetInfo &STI) { return *new (MCSubtargetAllocator.Allocate()) MCSubtargetInfo(STI); } diff --git a/llvm/lib/MC/MCOMFStreamer.cpp b/llvm/lib/MC/MCOMFStreamer.cpp new file mode 100644 index 00000000000000..b0a997f1307338 --- /dev/null +++ b/llvm/lib/MC/MCOMFStreamer.cpp @@ -0,0 +1,48 @@ +//===- lib/MC/MCOMFStreamer.cpp - OMF Object Output -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file assembles .s files and emits OMF .o object files. +// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/MCOMFStreamer.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +bool MCOMFStreamer::emitSymbolAttribute(MCSymbol *Symbol, + MCSymbolAttr Attribute) { + llvm_unreachable("Unimplemented!"); +} + +void MCOMFStreamer::emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, + unsigned ByteAlignment) { + llvm_unreachable("Unimplemented!"); +} + +void MCOMFStreamer::emitZerofill(MCSection *Section, MCSymbol *Symbol, + uint64_t Size, unsigned ByteAlignment, + SMLoc Loc) { + llvm_unreachable("Unimplemented!"); +} + +void MCOMFStreamer::emitInstToData(const MCInst &Inst, + const MCSubtargetInfo &) { + llvm_unreachable("Unimplemented!"); +} + +MCStreamer *llvm::createOMFStreamer(MCContext &Context, + std::unique_ptr &&MAB, + std::unique_ptr &&OW, + std::unique_ptr &&CE, + bool RelaxAll) { + return new MCOMFStreamer(Context, std::move(MAB), std::move(OW), std::move(CE)); +} diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp index b77a9635f64c40..f4210071a3622b 100644 --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -17,6 +17,7 @@ #include "llvm/MC/MCSectionCOFF.h" #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCSectionOMF.h" #include "llvm/MC/MCSectionWasm.h" #include "llvm/MC/MCSectionXCOFF.h" @@ -858,6 +859,13 @@ void MCObjectFileInfo::initXCOFFMCObjectFileInfo(const Triple &T) { DwarfMacinfoSection = nullptr; // SSUBTYP_DWMAC } +void MCObjectFileInfo::initOMFMCObjectFileInfo(const Triple &T) { + TextSection = Ctx->getOMFSection("CODE", SectionKind::getText()); + DataSection = Ctx->getOMFSection("DATA", SectionKind::getData()); + BSSSection = Ctx->getOMFSection("BSS", SectionKind::getBSS()); + ReadOnlySection = Ctx->getOMFSection("TEXT", SectionKind::getReadOnly()); +} + void MCObjectFileInfo::InitMCObjectFileInfo(const Triple &TheTriple, bool PIC, MCContext &ctx, bool LargeCodeModel) { @@ -908,6 +916,10 @@ void MCObjectFileInfo::InitMCObjectFileInfo(const Triple &TheTriple, bool PIC, Env = IsXCOFF; initXCOFFMCObjectFileInfo(TT); break; + case Triple::OMF: + Env = IsOMF; + initOMFMCObjectFileInfo(TT); + break; case Triple::UnknownObjectFormat: report_fatal_error("Cannot initialize MC for unknown object file format."); break; @@ -924,6 +936,7 @@ MCSection *MCObjectFileInfo::getDwarfComdatSection(const char *Name, case Triple::COFF: case Triple::Wasm: case Triple::XCOFF: + case Triple::OMF: case Triple::UnknownObjectFormat: report_fatal_error("Cannot get DWARF comdat section for this object file " "format: not implemented."); diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp index 9e92ce09986f68..7cbcef1bfb2192 100644 --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -728,6 +728,10 @@ AsmParser::AsmParser(SourceMgr &SM, MCContext &Ctx, MCStreamer &Out, report_fatal_error( "Need to implement createXCOFFAsmParser for XCOFF format."); break; + case MCObjectFileInfo::IsOMF: + report_fatal_error( + "Need to implement createOMFAsmParser for OMF format."); + break; } PlatformParser->Initialize(*this); diff --git a/llvm/lib/MC/MCSectionOMF.cpp b/llvm/lib/MC/MCSectionOMF.cpp new file mode 100644 index 00000000000000..e36db1ac7ae006 --- /dev/null +++ b/llvm/lib/MC/MCSectionOMF.cpp @@ -0,0 +1,27 @@ +//===- lib/MC/MCSectionOMF.cpp - OMF Code Section Representation ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/MCSectionOMF.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +MCSectionOMF::MCSectionOMF(StringRef Name, SectionKind K, MCSymbol *Begin) + : MCSection(SV_OMF, Name, K, Begin) {} + +MCSectionOMF::~MCSectionOMF() {} // anchor. + +void MCSectionOMF::PrintSwitchToSection(const MCAsmInfo &MAI, const Triple &T, + raw_ostream &OS, + const MCExpr *Subsection) const { + assert(!Subsection && "Unimplemented!"); + OS << "\tSEGMENT\t" << getSectionName() << '\n'; +} diff --git a/llvm/lib/Object/Binary.cpp b/llvm/lib/Object/Binary.cpp index 944d2bc1bca77e..28662bf07aaf4a 100644 --- a/llvm/lib/Object/Binary.cpp +++ b/llvm/lib/Object/Binary.cpp @@ -89,6 +89,9 @@ Expected> object::createBinary(MemoryBufferRef Buffer, return MinidumpFile::create(Buffer); case file_magic::tapi_file: return TapiUniversal::create(Buffer); + case file_magic::omf_object: + case file_magic::omf_archive: + return errorCodeToError(object_error::invalid_file_type); } llvm_unreachable("Unexpected Binary File Type"); } diff --git a/llvm/lib/Object/ObjectFile.cpp b/llvm/lib/Object/ObjectFile.cpp index 61b36ea0f448ae..82d45d7b605f5b 100644 --- a/llvm/lib/Object/ObjectFile.cpp +++ b/llvm/lib/Object/ObjectFile.cpp @@ -146,8 +146,9 @@ ObjectFile::createObjectFile(MemoryBufferRef Object, file_magic Type) { case file_magic::windows_resource: case file_magic::pdb: case file_magic::minidump: - return errorCodeToError(object_error::invalid_file_type); case file_magic::tapi_file: + case file_magic::omf_object: + case file_magic::omf_archive: return errorCodeToError(object_error::invalid_file_type); case file_magic::elf: case file_magic::elf_relocatable: diff --git a/llvm/lib/Object/SymbolicFile.cpp b/llvm/lib/Object/SymbolicFile.cpp index 3db4ad9ed14bd6..0293aa87f45c9b 100644 --- a/llvm/lib/Object/SymbolicFile.cpp +++ b/llvm/lib/Object/SymbolicFile.cpp @@ -54,6 +54,8 @@ SymbolicFile::createSymbolicFile(MemoryBufferRef Object, file_magic Type, case file_magic::pdb: case file_magic::minidump: case file_magic::tapi_file: + case file_magic::omf_object: + case file_magic::omf_archive: return errorCodeToError(object_error::invalid_file_type); case file_magic::elf: case file_magic::elf_executable: diff --git a/llvm/lib/Support/Triple.cpp b/llvm/lib/Support/Triple.cpp index 75ec257e15b51a..4c105923d07934 100644 --- a/llvm/lib/Support/Triple.cpp +++ b/llvm/lib/Support/Triple.cpp @@ -650,6 +650,7 @@ static StringRef getObjectFormatTypeName(Triple::ObjectFormatType Kind) { case Triple::COFF: return "coff"; case Triple::ELF: return "elf"; case Triple::MachO: return "macho"; + case Triple::OMF: return "omf"; case Triple::Wasm: return "wasm"; case Triple::XCOFF: return "xcoff"; } diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index 05f870b90ecdb0..b71e8a1c99db7e 100644 --- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -6264,6 +6264,7 @@ bool ARMAsmParser::parsePrefix(ARMMCExpr::VariantKind &RefKind) { CurrentFormat = WASM; break; case MCObjectFileInfo::IsXCOFF: + case MCObjectFileInfo::IsOMF: llvm_unreachable("unexpected object format"); break; } diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp index b45be99bece3b2..bcb8de215d8872 100644 --- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -1951,6 +1951,7 @@ StringRef ModuleAddressSanitizer::getGlobalMetadataSection() const { case Triple::COFF: return ".ASAN$GL"; case Triple::ELF: return "asan_globals"; case Triple::MachO: return "__DATA,__asan_globals,regular"; + case Triple::OMF: case Triple::Wasm: case Triple::XCOFF: report_fatal_error( From b2047745576eee2c52c1ed6efcea8b4936296ed1 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 20 Oct 2019 16:50:50 +0200 Subject: [PATCH 12/21] [Z80] Add Z80 target. --- llvm/include/llvm/ADT/Triple.h | 9 +- llvm/include/llvm/CodeGen/CallingConvLower.h | 4 + llvm/include/llvm/CodeGen/ValueTypes.h | 12 + llvm/include/llvm/IR/CallingConv.h | 9 + llvm/include/llvm/IR/RuntimeLibcalls.def | 68 + llvm/include/llvm/MC/MCAsmInfo.h | 12 +- llvm/include/llvm/MC/MCDirectives.h | 1 + llvm/lib/MC/MCAsmInfo.cpp | 2 + llvm/lib/MC/MCAsmStreamer.cpp | 10 + llvm/lib/MC/MCELFStreamer.cpp | 1 + llvm/lib/MC/MCMachOStreamer.cpp | 1 + llvm/lib/MC/MCWinCOFFStreamer.cpp | 1 + llvm/lib/Support/Triple.cpp | 25 + .../ARM/MCTargetDesc/ARMELFStreamer.cpp | 1 + llvm/lib/Target/LLVMBuild.txt | 1 + llvm/lib/Target/Z80/CMakeLists.txt | 29 + llvm/lib/Target/Z80/LLVMBuild.txt | 32 + .../Target/Z80/MCTargetDesc/CMakeLists.txt | 8 + .../Z80/MCTargetDesc/EZ80InstPrinter.cpp | 23 + .../Target/Z80/MCTargetDesc/EZ80InstPrinter.h | 36 + .../lib/Target/Z80/MCTargetDesc/LLVMBuild.txt | 22 + .../Z80/MCTargetDesc/Z80InstPrinter.cpp | 23 + .../Target/Z80/MCTargetDesc/Z80InstPrinter.h | 36 + .../Z80/MCTargetDesc/Z80InstPrinterCommon.cpp | 114 ++ .../Z80/MCTargetDesc/Z80InstPrinterCommon.h | 47 + .../Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp | 57 + .../Target/Z80/MCTargetDesc/Z80MCAsmInfo.h | 32 + .../Z80/MCTargetDesc/Z80MCTargetDesc.cpp | 94 ++ .../Target/Z80/MCTargetDesc/Z80MCTargetDesc.h | 81 + .../Z80/MCTargetDesc/Z80TargetStreamer.cpp | 47 + .../Z80/MCTargetDesc/Z80TargetStreamer.h | 55 + llvm/lib/Target/Z80/TargetInfo/CMakeLists.txt | 3 + llvm/lib/Target/Z80/TargetInfo/LLVMBuild.txt | 22 + .../Target/Z80/TargetInfo/Z80TargetInfo.cpp | 28 + .../lib/Target/Z80/TargetInfo/Z80TargetInfo.h | 21 + llvm/lib/Target/Z80/Z80.h | 26 + llvm/lib/Target/Z80/Z80.td | 92 ++ llvm/lib/Target/Z80/Z80AsmPrinter.cpp | 104 ++ llvm/lib/Target/Z80/Z80AsmPrinter.h | 42 + llvm/lib/Target/Z80/Z80CallingConv.cpp | 19 + llvm/lib/Target/Z80/Z80CallingConv.h | 32 + llvm/lib/Target/Z80/Z80CallingConv.td | 126 ++ llvm/lib/Target/Z80/Z80FrameLowering.cpp | 418 +++++ llvm/lib/Target/Z80/Z80FrameLowering.h | 92 ++ llvm/lib/Target/Z80/Z80ISelLowering.cpp | 534 ++++++ llvm/lib/Target/Z80/Z80ISelLowering.h | 129 ++ llvm/lib/Target/Z80/Z80InstrFormats.td | 174 ++ llvm/lib/Target/Z80/Z80InstrInfo.cpp | 1471 +++++++++++++++++ llvm/lib/Target/Z80/Z80InstrInfo.h | 201 +++ llvm/lib/Target/Z80/Z80InstrInfo.td | 1076 ++++++++++++ llvm/lib/Target/Z80/Z80MCInstLower.cpp | 114 ++ llvm/lib/Target/Z80/Z80MachineFunctionInfo.h | 61 + llvm/lib/Target/Z80/Z80RegisterInfo.cpp | 399 +++++ llvm/lib/Target/Z80/Z80RegisterInfo.h | 123 ++ llvm/lib/Target/Z80/Z80RegisterInfo.td | 127 ++ llvm/lib/Target/Z80/Z80Subtarget.cpp | 41 + llvm/lib/Target/Z80/Z80Subtarget.h | 95 ++ llvm/lib/Target/Z80/Z80TargetMachine.cpp | 169 ++ llvm/lib/Target/Z80/Z80TargetMachine.h | 43 + 59 files changed, 6671 insertions(+), 4 deletions(-) create mode 100644 llvm/lib/Target/Z80/CMakeLists.txt create mode 100644 llvm/lib/Target/Z80/LLVMBuild.txt create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/CMakeLists.txt create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/EZ80InstPrinter.cpp create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/EZ80InstPrinter.h create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/LLVMBuild.txt create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinter.cpp create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinter.h create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinterCommon.cpp create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinterCommon.h create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.h create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.cpp create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.h create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.cpp create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.h create mode 100644 llvm/lib/Target/Z80/TargetInfo/CMakeLists.txt create mode 100644 llvm/lib/Target/Z80/TargetInfo/LLVMBuild.txt create mode 100644 llvm/lib/Target/Z80/TargetInfo/Z80TargetInfo.cpp create mode 100644 llvm/lib/Target/Z80/TargetInfo/Z80TargetInfo.h create mode 100644 llvm/lib/Target/Z80/Z80.h create mode 100644 llvm/lib/Target/Z80/Z80.td create mode 100644 llvm/lib/Target/Z80/Z80AsmPrinter.cpp create mode 100644 llvm/lib/Target/Z80/Z80AsmPrinter.h create mode 100644 llvm/lib/Target/Z80/Z80CallingConv.cpp create mode 100644 llvm/lib/Target/Z80/Z80CallingConv.h create mode 100644 llvm/lib/Target/Z80/Z80CallingConv.td create mode 100644 llvm/lib/Target/Z80/Z80FrameLowering.cpp create mode 100644 llvm/lib/Target/Z80/Z80FrameLowering.h create mode 100644 llvm/lib/Target/Z80/Z80ISelLowering.cpp create mode 100644 llvm/lib/Target/Z80/Z80ISelLowering.h create mode 100644 llvm/lib/Target/Z80/Z80InstrFormats.td create mode 100644 llvm/lib/Target/Z80/Z80InstrInfo.cpp create mode 100644 llvm/lib/Target/Z80/Z80InstrInfo.h create mode 100644 llvm/lib/Target/Z80/Z80InstrInfo.td create mode 100644 llvm/lib/Target/Z80/Z80MCInstLower.cpp create mode 100644 llvm/lib/Target/Z80/Z80MachineFunctionInfo.h create mode 100644 llvm/lib/Target/Z80/Z80RegisterInfo.cpp create mode 100644 llvm/lib/Target/Z80/Z80RegisterInfo.h create mode 100644 llvm/lib/Target/Z80/Z80RegisterInfo.td create mode 100644 llvm/lib/Target/Z80/Z80Subtarget.cpp create mode 100644 llvm/lib/Target/Z80/Z80Subtarget.h create mode 100644 llvm/lib/Target/Z80/Z80TargetMachine.cpp create mode 100644 llvm/lib/Target/Z80/Z80TargetMachine.h diff --git a/llvm/include/llvm/ADT/Triple.h b/llvm/include/llvm/ADT/Triple.h index d47e1b4f2c07d8..6f89b9e8c31a50 100644 --- a/llvm/include/llvm/ADT/Triple.h +++ b/llvm/include/llvm/ADT/Triple.h @@ -98,7 +98,9 @@ class Triple { renderscript32, // 32-bit RenderScript renderscript64, // 64-bit RenderScript ve, // NEC SX-Aurora Vector Engine - LastArchType = ve + z80, // Z80 + ez80, // eZ80 + LastArchType = ez80 }; enum SubArchType { NoSubArch, @@ -752,6 +754,11 @@ class Triple { return getArch() == Triple::wasm32 || getArch() == Triple::wasm64; } + /// Tests whether the target is (e)z80. + bool isZ80() const { + return getArch() == Triple::z80 || getArch() == Triple::ez80; + } + /// Tests whether the target supports comdat bool supportsCOMDAT() const { return !(isOSBinFormatMachO() || isOSBinFormatXCOFF()); diff --git a/llvm/include/llvm/CodeGen/CallingConvLower.h b/llvm/include/llvm/CodeGen/CallingConvLower.h index cc3a75c897d3d4..b8393c4850076c 100644 --- a/llvm/include/llvm/CodeGen/CallingConvLower.h +++ b/llvm/include/llvm/CodeGen/CallingConvLower.h @@ -138,6 +138,10 @@ class CCValAssign { isMem = true; } + void setLocInfo(LocInfo NewHTP) { + HTP = NewHTP; + } + unsigned getValNo() const { return ValNo; } MVT getValVT() const { return ValVT; } diff --git a/llvm/include/llvm/CodeGen/ValueTypes.h b/llvm/include/llvm/CodeGen/ValueTypes.h index db8161caf7d2e9..1d2576df33a51f 100644 --- a/llvm/include/llvm/CodeGen/ValueTypes.h +++ b/llvm/include/llvm/CodeGen/ValueTypes.h @@ -343,6 +343,18 @@ namespace llvm { return getStoreSize() * 8; } + /// getNumParts - Return the number of parts with PartBits bits that make up + /// this VT. + unsigned getNumParts(unsigned PartBits) const { + return (getSizeInBits() + PartBits - 1) / PartBits; + } + + /// getNumParts - Return the number of parts of type PartVT that make up + /// this VT. + unsigned getNumParts(EVT PartVT) const { + return getNumParts(PartVT.getSizeInBits()); + } + /// Rounds the bit-width of the given integer EVT up to the nearest power of /// two (and at least to eight), and returns the integer EVT with that /// number of bits. diff --git a/llvm/include/llvm/IR/CallingConv.h b/llvm/include/llvm/IR/CallingConv.h index d0906de3ea4e11..c94490c85d8ec1 100644 --- a/llvm/include/llvm/IR/CallingConv.h +++ b/llvm/include/llvm/IR/CallingConv.h @@ -241,6 +241,15 @@ namespace CallingConv { /// The remainder matches the regular calling convention. WASM_EmscriptenInvoke = 99, + /// Calling conventions used for special Z80 rtlib functions + /// which pass in registers and save all registers. + Z80_LibCall = 100, + Z80_LibCall_AB = 101, + Z80_LibCall_AC = 102, + Z80_LibCall_BC = 103, + Z80_LibCall_L = 104, + Z80_LibCall_F = 105, + /// The highest possible calling convention ID. Must be some 2^k - 1. MaxID = 1023 }; diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.def b/llvm/include/llvm/IR/RuntimeLibcalls.def index fd5c0fc7aba0a6..ec32e94708b782 100644 --- a/llvm/include/llvm/IR/RuntimeLibcalls.def +++ b/llvm/include/llvm/IR/RuntimeLibcalls.def @@ -30,20 +30,71 @@ #endif // Integer +HANDLE_LIBCALL(ZEXT_I16_I24, nullptr) +HANDLE_LIBCALL(SEXT_I16_I24, nullptr) +HANDLE_LIBCALL(SEXT_I24_I32, nullptr) +HANDLE_LIBCALL(NOT_I16, nullptr) +HANDLE_LIBCALL(NOT_I24, nullptr) +HANDLE_LIBCALL(NOT_I32, nullptr) +HANDLE_LIBCALL(NOT_I64, nullptr) +HANDLE_LIBCALL(AND_I8, nullptr) +HANDLE_LIBCALL(AND_I16, nullptr) +HANDLE_LIBCALL(AND_I24, nullptr) +HANDLE_LIBCALL(AND_I32, nullptr) +HANDLE_LIBCALL(AND_I64, nullptr) +HANDLE_LIBCALL(AND_I128, nullptr) +HANDLE_LIBCALL(OR_I8, nullptr) +HANDLE_LIBCALL(OR_I16, nullptr) +HANDLE_LIBCALL(OR_I24, nullptr) +HANDLE_LIBCALL(OR_I32, nullptr) +HANDLE_LIBCALL(OR_I64, nullptr) +HANDLE_LIBCALL(OR_I128, nullptr) +HANDLE_LIBCALL(XOR_I8, nullptr) +HANDLE_LIBCALL(XOR_I16, nullptr) +HANDLE_LIBCALL(XOR_I24, nullptr) +HANDLE_LIBCALL(XOR_I32, nullptr) +HANDLE_LIBCALL(XOR_I64, nullptr) +HANDLE_LIBCALL(XOR_I128, nullptr) +HANDLE_LIBCALL(SHL_I8, nullptr) HANDLE_LIBCALL(SHL_I16, "__ashlhi3") +HANDLE_LIBCALL(SHL_I24, nullptr) HANDLE_LIBCALL(SHL_I32, "__ashlsi3") HANDLE_LIBCALL(SHL_I64, "__ashldi3") HANDLE_LIBCALL(SHL_I128, "__ashlti3") +HANDLE_LIBCALL(SHL_I16_I8, nullptr) +HANDLE_LIBCALL(SHL_I24_I8, nullptr) +HANDLE_LIBCALL(SRL_I8, nullptr) HANDLE_LIBCALL(SRL_I16, "__lshrhi3") +HANDLE_LIBCALL(SRL_I16_I8, nullptr) +HANDLE_LIBCALL(SRL_I24, nullptr) +HANDLE_LIBCALL(SRL_I24_I8, nullptr) HANDLE_LIBCALL(SRL_I32, "__lshrsi3") HANDLE_LIBCALL(SRL_I64, "__lshrdi3") HANDLE_LIBCALL(SRL_I128, "__lshrti3") +HANDLE_LIBCALL(SRA_I8, nullptr) HANDLE_LIBCALL(SRA_I16, "__ashrhi3") +HANDLE_LIBCALL(SRA_I16_I8, nullptr) +HANDLE_LIBCALL(SRA_I24, nullptr) +HANDLE_LIBCALL(SRA_I24_I8, nullptr) HANDLE_LIBCALL(SRA_I32, "__ashrsi3") HANDLE_LIBCALL(SRA_I64, "__ashrdi3") HANDLE_LIBCALL(SRA_I128, "__ashrti3") +HANDLE_LIBCALL(CMP_I32, nullptr) +HANDLE_LIBCALL(CMP_I64, nullptr) +HANDLE_LIBCALL(CMP_I16_0, nullptr) +HANDLE_LIBCALL(CMP_I24_0, nullptr) +HANDLE_LIBCALL(CMP_I32_0, nullptr) +HANDLE_LIBCALL(CMP_I64_0, nullptr) +HANDLE_LIBCALL(SCMP, nullptr) +HANDLE_LIBCALL(ADD_I32, nullptr) +HANDLE_LIBCALL(ADD_I32_I8, nullptr) +HANDLE_LIBCALL(ADD_I64, nullptr) +HANDLE_LIBCALL(SUB_I32, nullptr) +HANDLE_LIBCALL(SUB_I64, nullptr) HANDLE_LIBCALL(MUL_I8, "__mulqi3") HANDLE_LIBCALL(MUL_I16, "__mulhi3") +HANDLE_LIBCALL(MUL_I24, nullptr) +HANDLE_LIBCALL(MUL_I24_I8, nullptr) HANDLE_LIBCALL(MUL_I32, "__mulsi3") HANDLE_LIBCALL(MUL_I64, "__muldi3") HANDLE_LIBCALL(MUL_I128, "__multi3") @@ -52,34 +103,42 @@ HANDLE_LIBCALL(MULO_I64, "__mulodi4") HANDLE_LIBCALL(MULO_I128, "__muloti4") HANDLE_LIBCALL(SDIV_I8, "__divqi3") HANDLE_LIBCALL(SDIV_I16, "__divhi3") +HANDLE_LIBCALL(SDIV_I24, nullptr) HANDLE_LIBCALL(SDIV_I32, "__divsi3") HANDLE_LIBCALL(SDIV_I64, "__divdi3") HANDLE_LIBCALL(SDIV_I128, "__divti3") HANDLE_LIBCALL(UDIV_I8, "__udivqi3") HANDLE_LIBCALL(UDIV_I16, "__udivhi3") +HANDLE_LIBCALL(UDIV_I24, nullptr) HANDLE_LIBCALL(UDIV_I32, "__udivsi3") HANDLE_LIBCALL(UDIV_I64, "__udivdi3") HANDLE_LIBCALL(UDIV_I128, "__udivti3") HANDLE_LIBCALL(SREM_I8, "__modqi3") HANDLE_LIBCALL(SREM_I16, "__modhi3") +HANDLE_LIBCALL(SREM_I24, nullptr) HANDLE_LIBCALL(SREM_I32, "__modsi3") HANDLE_LIBCALL(SREM_I64, "__moddi3") HANDLE_LIBCALL(SREM_I128, "__modti3") HANDLE_LIBCALL(UREM_I8, "__umodqi3") HANDLE_LIBCALL(UREM_I16, "__umodhi3") +HANDLE_LIBCALL(UREM_I24, nullptr) HANDLE_LIBCALL(UREM_I32, "__umodsi3") HANDLE_LIBCALL(UREM_I64, "__umoddi3") HANDLE_LIBCALL(UREM_I128, "__umodti3") HANDLE_LIBCALL(SDIVREM_I8, nullptr) HANDLE_LIBCALL(SDIVREM_I16, nullptr) +HANDLE_LIBCALL(SDIVREM_I24, nullptr) HANDLE_LIBCALL(SDIVREM_I32, nullptr) HANDLE_LIBCALL(SDIVREM_I64, nullptr) HANDLE_LIBCALL(SDIVREM_I128, nullptr) HANDLE_LIBCALL(UDIVREM_I8, nullptr) HANDLE_LIBCALL(UDIVREM_I16, nullptr) +HANDLE_LIBCALL(UDIVREM_I24, nullptr) HANDLE_LIBCALL(UDIVREM_I32, nullptr) HANDLE_LIBCALL(UDIVREM_I64, nullptr) HANDLE_LIBCALL(UDIVREM_I128, nullptr) +HANDLE_LIBCALL(NEG_I16, nullptr) +HANDLE_LIBCALL(NEG_I24, nullptr) HANDLE_LIBCALL(NEG_I32, "__negsi2") HANDLE_LIBCALL(NEG_I64, "__negdi2") HANDLE_LIBCALL(CTLZ_I32, "__clzsi2") @@ -90,6 +149,11 @@ HANDLE_LIBCALL(POPCNT_I16, nullptr) HANDLE_LIBCALL(POPCNT_I24, nullptr) HANDLE_LIBCALL(POPCNT_I32, "__popcountsi2") HANDLE_LIBCALL(POPCNT_I64, "__popcountdi2") +HANDLE_LIBCALL(BITREV_I8, nullptr) +HANDLE_LIBCALL(BITREV_I16, nullptr) +HANDLE_LIBCALL(BITREV_I24, nullptr) +HANDLE_LIBCALL(BITREV_I32, nullptr) +HANDLE_LIBCALL(BITREV_I64, nullptr) // Floating-point HANDLE_LIBCALL(ADD_F32, "__addsf3") @@ -284,6 +348,10 @@ HANDLE_LIBCALL(LLRINT_F64, "llrint") HANDLE_LIBCALL(LLRINT_F80, "llrintl") HANDLE_LIBCALL(LLRINT_F128, "llrintl") HANDLE_LIBCALL(LLRINT_PPCF128, "llrintl") +HANDLE_LIBCALL(NEG_F32, nullptr) +HANDLE_LIBCALL(NEG_F64, nullptr) +HANDLE_LIBCALL(CMP_F32, nullptr) +HANDLE_LIBCALL(CMP_F64, nullptr) // Conversion HANDLE_LIBCALL(FPEXT_F32_PPCF128, "__gcc_stoq") diff --git a/llvm/include/llvm/MC/MCAsmInfo.h b/llvm/include/llvm/MC/MCAsmInfo.h index 974155ff3319db..42234ee56f11a9 100644 --- a/llvm/include/llvm/MC/MCAsmInfo.h +++ b/llvm/include/llvm/MC/MCAsmInfo.h @@ -147,9 +147,10 @@ class MCAsmInfo { const char *InlineAsmEnd; /// These are assembly directives that tells the assembler to interpret the - /// following instructions differently. Defaults to ".code16", ".code32", - /// ".code64". + /// following instructions differently. Defaults to ".code16", ".code24", + /// ".code32", ".code64". const char *Code16Directive; + const char *Code24Directive; const char *Code32Directive; const char *Code64Directive; @@ -203,9 +204,10 @@ class MCAsmInfo { /// These directives are used to output some unit of integer data to the /// current section. If a data directive is set to null, smaller data /// directives will be used to emit the large sizes. Defaults to "\t.byte\t", - /// "\t.short\t", "\t.long\t", "\t.quad\t" + /// "\t.short\t", nullptr, "\t.long\t", "\t.quad\t" const char *Data8bitsDirective; const char *Data16bitsDirective; + const char *Data24bitsDirective; const char *Data32bitsDirective; const char *Data64bitsDirective; @@ -438,6 +440,7 @@ class MCAsmInfo { const char *getData8bitsDirective() const { return Data8bitsDirective; } const char *getData16bitsDirective() const { return Data16bitsDirective; } + const char *getData24bitsDirective() const { return Data24bitsDirective; } const char *getData32bitsDirective() const { return Data32bitsDirective; } const char *getData64bitsDirective() const { return Data64bitsDirective; } const char *getGPRel64Directive() const { return GPRel64Directive; } @@ -541,6 +544,7 @@ class MCAsmInfo { const char *getInlineAsmStart() const { return InlineAsmStart; } const char *getInlineAsmEnd() const { return InlineAsmEnd; } const char *getCode16Directive() const { return Code16Directive; } + const char *getCode24Directive() const { return Code24Directive; } const char *getCode32Directive() const { return Code32Directive; } const char *getCode64Directive() const { return Code64Directive; } unsigned getAssemblerDialect() const { return AssemblerDialect; } @@ -560,10 +564,12 @@ class MCAsmInfo { bool doesZeroDirectiveSupportNonZeroValue() const { return ZeroDirectiveSupportsNonZeroValue; } + virtual const char *getBlockDirective(int64_t Size) const { return nullptr; } const char *getAsciiDirective() const { return AsciiDirective; } const char *getAscizDirective() const { return AscizDirective; } bool getAlignmentIsInBytes() const { return AlignmentIsInBytes; } unsigned getTextAlignFillValue() const { return TextAlignFillValue; } + const char *getGlobalDirective() const { return GlobalDirective; } bool doesSetDirectiveSuppressReloc() const { diff --git a/llvm/include/llvm/MC/MCDirectives.h b/llvm/include/llvm/MC/MCDirectives.h index 51e57ad3702155..30c9fc5d7d13b4 100644 --- a/llvm/include/llvm/MC/MCDirectives.h +++ b/llvm/include/llvm/MC/MCDirectives.h @@ -51,6 +51,7 @@ enum MCAssemblerFlag { MCAF_SyntaxUnified, ///< .syntax (ARM/ELF) MCAF_SubsectionsViaSymbols, ///< .subsections_via_symbols (MachO) MCAF_Code16, ///< .code16 (X86) / .code 16 (ARM) + MCAF_Code24, ///< .assume adl=1 (eZ80) MCAF_Code32, ///< .code32 (X86) / .code 32 (ARM) MCAF_Code64 ///< .code64 (X86) }; diff --git a/llvm/lib/MC/MCAsmInfo.cpp b/llvm/lib/MC/MCAsmInfo.cpp index 9767ee6c1133ae..3184c218a1bb3a 100644 --- a/llvm/lib/MC/MCAsmInfo.cpp +++ b/llvm/lib/MC/MCAsmInfo.cpp @@ -38,12 +38,14 @@ MCAsmInfo::MCAsmInfo() { InlineAsmStart = "APP"; InlineAsmEnd = "NO_APP"; Code16Directive = ".code16"; + Code24Directive = ".code24"; Code32Directive = ".code32"; Code64Directive = ".code64"; ZeroDirective = "\t.zero\t"; AsciiDirective = "\t.ascii\t"; AscizDirective = "\t.asciz\t"; Data8bitsDirective = "\t.byte\t"; + Data24bitsDirective = nullptr; Data16bitsDirective = "\t.short\t"; Data32bitsDirective = "\t.long\t"; Data64bitsDirective = "\t.quad\t"; diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index 9a86895a2fe13f..700937e854ada3 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -504,6 +504,7 @@ void MCAsmStreamer::emitAssemblerFlag(MCAssemblerFlag Flag) { case MCAF_SyntaxUnified: OS << "\t.syntax unified"; break; case MCAF_SubsectionsViaSymbols: OS << ".subsections_via_symbols"; break; case MCAF_Code16: OS << '\t'<< MAI->getCode16Directive();break; + case MCAF_Code24: OS << '\t'<< MAI->getCode24Directive();break; case MCAF_Code32: OS << '\t'<< MAI->getCode32Directive();break; case MCAF_Code64: OS << '\t'<< MAI->getCode64Directive();break; } @@ -1033,6 +1034,7 @@ void MCAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, default: break; case 1: Directive = MAI->getData8bitsDirective(); break; case 2: Directive = MAI->getData16bitsDirective(); break; + case 3: Directive = MAI->getData24bitsDirective(); break; case 4: Directive = MAI->getData32bitsDirective(); break; case 8: Directive = MAI->getData64bitsDirective(); break; } @@ -1172,6 +1174,14 @@ void MCAsmStreamer::emitFill(const MCExpr &NumBytes, uint64_t FillValue, return; } + if (const char *BlockDirective = MAI->getBlockDirective(1)) { + OS << BlockDirective; + NumBytes.print(OS, MAI); + OS << ", " << FillValue; + EmitEOL(); + return; + } + MCStreamer::emitFill(NumBytes, FillValue); } diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp index 49d863f258bf98..573a7797a2cd4a 100644 --- a/llvm/lib/MC/MCELFStreamer.cpp +++ b/llvm/lib/MC/MCELFStreamer.cpp @@ -124,6 +124,7 @@ void MCELFStreamer::emitAssemblerFlag(MCAssemblerFlag Flag) { switch (Flag) { case MCAF_SyntaxUnified: return; // no-op here. case MCAF_Code16: return; // Change parsing mode; no-op here. + case MCAF_Code24: return; // Change parsing mode; no-op here. case MCAF_Code32: return; // Change parsing mode; no-op here. case MCAF_Code64: return; // Change parsing mode; no-op here. case MCAF_SubsectionsViaSymbols: diff --git a/llvm/lib/MC/MCMachOStreamer.cpp b/llvm/lib/MC/MCMachOStreamer.cpp index 2b1d1b28ea184d..3a35947d625951 100644 --- a/llvm/lib/MC/MCMachOStreamer.cpp +++ b/llvm/lib/MC/MCMachOStreamer.cpp @@ -238,6 +238,7 @@ void MCMachOStreamer::emitAssemblerFlag(MCAssemblerFlag Flag) { switch (Flag) { case MCAF_SyntaxUnified: return; // no-op here. case MCAF_Code16: return; // Change parsing mode; no-op here. + case MCAF_Code24: return; // Change parsing mode; no-op here. case MCAF_Code32: return; // Change parsing mode; no-op here. case MCAF_Code64: return; // Change parsing mode; no-op here. case MCAF_SubsectionsViaSymbols: diff --git a/llvm/lib/MC/MCWinCOFFStreamer.cpp b/llvm/lib/MC/MCWinCOFFStreamer.cpp index 7f0f7fccd54268..facfd9bbb55d31 100644 --- a/llvm/lib/MC/MCWinCOFFStreamer.cpp +++ b/llvm/lib/MC/MCWinCOFFStreamer.cpp @@ -95,6 +95,7 @@ void MCWinCOFFStreamer::emitAssemblerFlag(MCAssemblerFlag Flag) { // None of these require COFF specific handling. case MCAF_SyntaxUnified: case MCAF_Code16: + case MCAF_Code24: case MCAF_Code32: case MCAF_Code64: break; diff --git a/llvm/lib/Support/Triple.cpp b/llvm/lib/Support/Triple.cpp index 4c105923d07934..f1f3ad2fc80499 100644 --- a/llvm/lib/Support/Triple.cpp +++ b/llvm/lib/Support/Triple.cpp @@ -75,6 +75,8 @@ StringRef Triple::getArchTypeName(ArchType Kind) { case x86: return "i386"; case x86_64: return "x86_64"; case xcore: return "xcore"; + case z80: return "z80"; + case ez80: return "ez80"; } llvm_unreachable("Invalid ArchType!"); @@ -151,6 +153,9 @@ StringRef Triple::getArchTypePrefix(ArchType Kind) { case riscv64: return "riscv"; case ve: return "ve"; + + case z80: + case ez80: return "z80"; } } @@ -321,6 +326,8 @@ Triple::ArchType Triple::getArchTypeForLLVMName(StringRef Name) { .Case("renderscript32", renderscript32) .Case("renderscript64", renderscript64) .Case("ve", ve) + .Case("z80", z80) + .Case("ez80", ez80) .Default(UnknownArch); } @@ -450,6 +457,8 @@ static Triple::ArchType parseArch(StringRef ArchName) { .Case("ve", Triple::ve) .Case("wasm32", Triple::wasm32) .Case("wasm64", Triple::wasm64) + .Cases("z80", "z180", Triple::z80) + .Case("ez80", Triple::ez80) .Default(Triple::UnknownArch); // Some architectures require special parsing logic just to compute the @@ -724,6 +733,10 @@ static Triple::ObjectFormatType getDefaultFormat(const Triple &T) { case Triple::wasm32: case Triple::wasm64: return Triple::Wasm; + + case Triple::z80: + case Triple::ez80: + return Triple::OMF; } llvm_unreachable("unknown architecture"); } @@ -1243,8 +1256,12 @@ static unsigned getArchPointerBitWidth(llvm::Triple::ArchType Arch) { case llvm::Triple::avr: case llvm::Triple::msp430: + case llvm::Triple::z80: return 16; + case llvm::Triple::ez80: + return 24; + case llvm::Triple::aarch64_32: case llvm::Triple::amdil: case llvm::Triple::arc: @@ -1325,6 +1342,8 @@ Triple Triple::get32BitArchVariant() const { case Triple::ppc64le: case Triple::systemz: case Triple::ve: + case Triple::z80: + case Triple::ez80: T.setArch(UnknownArch); break; @@ -1394,6 +1413,8 @@ Triple Triple::get64BitArchVariant() const { case Triple::tce: case Triple::tcele: case Triple::xcore: + case Triple::z80: + case Triple::ez80: T.setArch(UnknownArch); break; @@ -1477,6 +1498,8 @@ Triple Triple::getBigEndianArchVariant() const { case Triple::x86_64: case Triple::xcore: case Triple::ve: + case Triple::z80: + case Triple::ez80: // ARM is intentionally unsupported here, changing the architecture would // drop any arch suffixes. @@ -1569,6 +1592,8 @@ bool Triple::isLittleEndian() const { case Triple::x86: case Triple::x86_64: case Triple::xcore: + case Triple::z80: + case Triple::ez80: return true; default: return false; diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp index 876741d6c34386..eec6052333c4b4 100644 --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp @@ -572,6 +572,7 @@ class ARMELFStreamer : public MCELFStreamer { case MCAF_Code32: IsThumb = false; return; // Change to ARM mode + case MCAF_Code24: case MCAF_Code64: return; case MCAF_SubsectionsViaSymbols: diff --git a/llvm/lib/Target/LLVMBuild.txt b/llvm/lib/Target/LLVMBuild.txt index e5a9d787e7fa5c..b1119d26977da8 100644 --- a/llvm/lib/Target/LLVMBuild.txt +++ b/llvm/lib/Target/LLVMBuild.txt @@ -37,6 +37,7 @@ subdirectories = WebAssembly X86 XCore + Z80 ; This is a special group whose required libraries are extended (by llvm-build) ; with the best execution engine (the native JIT, if available, or the diff --git a/llvm/lib/Target/Z80/CMakeLists.txt b/llvm/lib/Target/Z80/CMakeLists.txt new file mode 100644 index 00000000000000..76a282b14807e9 --- /dev/null +++ b/llvm/lib/Target/Z80/CMakeLists.txt @@ -0,0 +1,29 @@ +set(LLVM_TARGET_DEFINITIONS Z80.td) + +tablegen(LLVM EZ80GenAsmWriter.inc -gen-asm-writer -asmwriternum=1) +tablegen(LLVM Z80GenAsmWriter.inc -gen-asm-writer) +tablegen(LLVM Z80GenCallingConv.inc -gen-callingconv) +tablegen(LLVM Z80GenGlobalISel.inc -gen-global-isel) +tablegen(LLVM Z80GenInstrInfo.inc -gen-instr-info) +tablegen(LLVM Z80GenRegisterBank.inc -gen-register-bank) +tablegen(LLVM Z80GenRegisterInfo.inc -gen-register-info) +tablegen(LLVM Z80GenSubtargetInfo.inc -gen-subtarget) + +add_public_tablegen_target(Z80CommonTableGen) + +set(sources + Z80AsmPrinter.cpp + Z80CallingConv.cpp + Z80FrameLowering.cpp + Z80InstrInfo.cpp + Z80ISelLowering.cpp + Z80MCInstLower.cpp + Z80RegisterInfo.cpp + Z80Subtarget.cpp + Z80TargetMachine.cpp + ) + +add_llvm_target(Z80CodeGen ${sources}) + +add_subdirectory(MCTargetDesc) +add_subdirectory(TargetInfo) diff --git a/llvm/lib/Target/Z80/LLVMBuild.txt b/llvm/lib/Target/Z80/LLVMBuild.txt new file mode 100644 index 00000000000000..0ab54abb9b6000 --- /dev/null +++ b/llvm/lib/Target/Z80/LLVMBuild.txt @@ -0,0 +1,32 @@ +;===- ./lib/Target/Z80/LLVMBuild.txt ---------------------------*- Conf -*--===; +; +; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +; See https://llvm.org/LICENSE.txt for license information. +; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[common] +subdirectories = MCTargetDesc TargetInfo + +[component_0] +type = TargetGroup +name = Z80 +parent = Target +has_asmparser = 1 +has_asmprinter = 1 + +[component_1] +type = Library +name = Z80CodeGen +parent = Z80 +required_libraries = AsmPrinter CodeGen Core GlobalISel MC SelectionDAG Support Target Z80Desc Z80Info +add_to_library_groups = Z80 diff --git a/llvm/lib/Target/Z80/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/Z80/MCTargetDesc/CMakeLists.txt new file mode 100644 index 00000000000000..1939a2c40dcede --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/CMakeLists.txt @@ -0,0 +1,8 @@ +add_llvm_component_library(LLVMZ80Desc + EZ80InstPrinter.cpp + Z80InstPrinter.cpp + Z80InstPrinterCommon.cpp + Z80MCAsmInfo.cpp + Z80MCTargetDesc.cpp + Z80TargetStreamer.cpp + ) diff --git a/llvm/lib/Target/Z80/MCTargetDesc/EZ80InstPrinter.cpp b/llvm/lib/Target/Z80/MCTargetDesc/EZ80InstPrinter.cpp new file mode 100644 index 00000000000000..a9576f80e3247b --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/EZ80InstPrinter.cpp @@ -0,0 +1,23 @@ +//===- EZ80InstPrinter.cpp - Convert EZ80 MCInst to assembly ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class prints an EZ80 MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#include "EZ80InstPrinter.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormattedStream.h" +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +// Include the auto-generated portion of the assembly writer. +#include "EZ80GenAsmWriter.inc" diff --git a/llvm/lib/Target/Z80/MCTargetDesc/EZ80InstPrinter.h b/llvm/lib/Target/Z80/MCTargetDesc/EZ80InstPrinter.h new file mode 100644 index 00000000000000..5b8f15e4147831 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/EZ80InstPrinter.h @@ -0,0 +1,36 @@ +//===- EZ80InstPrinter.h - Convert EZ80 MCInst to assembly ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class prints an EZ80 MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_INSTPRINTER_EZ80INSTPRINTER_H +#define LLVM_LIB_TARGET_Z80_INSTPRINTER_EZ80INSTPRINTER_H + +#include "Z80InstPrinterCommon.h" + +namespace llvm { + class Z80EInstPrinter final : public Z80InstPrinterCommon { + public: + Z80EInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, + const MCRegisterInfo &MRI) + : Z80InstPrinterCommon(MAI, MII, MRI) {} + + // Autogenerated by tblgen. + void printInstruction(const MCInst *MI, uint64_t Address, + raw_ostream &OS) override; + StringRef getRegName(unsigned RegNo) const override { + return getRegisterName(RegNo); + } + static const char *getRegisterName(unsigned RegNo); + }; +} + +#endif diff --git a/llvm/lib/Target/Z80/MCTargetDesc/LLVMBuild.txt b/llvm/lib/Target/Z80/MCTargetDesc/LLVMBuild.txt new file mode 100644 index 00000000000000..d1d1da727aa7d8 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./lib/Target/Z80/MCTargetDesc/LLVMBuild.txt --------------*- Conf -*--===; +; +; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +; See https://llvm.org/LICENSE.txt for license information. +; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = Z80Desc +parent = Z80 +required_libraries = MC MCDisassembler Object Support Z80Info +add_to_library_groups = Z80 diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinter.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinter.cpp new file mode 100644 index 00000000000000..b8a5abea46bed9 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinter.cpp @@ -0,0 +1,23 @@ +//===- Z80InstPrinter.cpp - Convert Z80 MCInst to assembly ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class prints a Z80 MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#include "Z80InstPrinter.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormattedStream.h" +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +// Include the auto-generated portion of the assembly writer. +#include "Z80GenAsmWriter.inc" diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinter.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinter.h new file mode 100644 index 00000000000000..93e9a2a6c7d71c --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinter.h @@ -0,0 +1,36 @@ +//===- Z80InstPrinter.h - Convert Z80 MCInst to assembly --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class prints a Z80 MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_INSTPRINTER_Z80INSTPRINTER_H +#define LLVM_LIB_TARGET_Z80_INSTPRINTER_Z80INSTPRINTER_H + +#include "Z80InstPrinterCommon.h" + +namespace llvm { + class Z80InstPrinter final : public Z80InstPrinterCommon { + public: + Z80InstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, + const MCRegisterInfo &MRI) + : Z80InstPrinterCommon(MAI, MII, MRI) {} + + // Autogenerated by tblgen. + void printInstruction(const MCInst *MI, uint64_t Address, + raw_ostream &OS) override; + StringRef getRegName(unsigned RegNo) const override { + return getRegisterName(RegNo); + } + static const char *getRegisterName(unsigned RegNo); + }; +} + +#endif diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinterCommon.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinterCommon.cpp new file mode 100644 index 00000000000000..df8515e75ba6ec --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinterCommon.cpp @@ -0,0 +1,114 @@ +//===- Z80InstPrinter.cpp - Convert Z80 MCInst to assembly ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file includes code common for rendering MCInst instances as (e)Z80 +// assembly. +// +//===----------------------------------------------------------------------===// + +#include "Z80InstPrinterCommon.h" +#include "Z80InstPrinter.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormattedStream.h" +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +void Z80InstPrinterCommon::printRegName(raw_ostream &OS, unsigned RegNo) const { + OS << markup(""); +} + +void Z80InstPrinterCommon::printInst(const MCInst *MI, uint64_t Address, + StringRef Annot, + const MCSubtargetInfo &STI, + raw_ostream &OS) { + printInstruction(MI, Address, OS); + printAnnotation(OS, Annot); +} + +void Z80InstPrinterCommon::printOperand(const MCInst *MI, unsigned OpNo, + raw_ostream &OS) { + const MCOperand &Op = MI->getOperand(OpNo); + if (Op.isReg()) { + printRegName(OS, Op.getReg()); + } else if (Op.isImm()) { + OS << Op.getImm(); + } else { + assert(Op.isExpr() && "unknown operand kind in printOperand"); + OS << markup("print(OS, &MAI); + OS << markup(">"); + } +} +void Z80InstPrinterCommon::printCCOperand(const MCInst *MI, unsigned Op, + raw_ostream &OS) { + switch (MI->getOperand(Op).getImm()) { + default: + llvm_unreachable("Invalid CC operand!"); + case 0: + OS << "nz"; + break; + case 1: + OS << "z"; + break; + case 2: + OS << "nc"; + break; + case 3: + OS << "c"; + break; + case 4: + OS << "po"; + break; + case 5: + OS << "pe"; + break; + case 6: + OS << "p"; + break; + case 7: + OS << "m"; + break; + } +} + +void Z80InstPrinterCommon::printMem(const MCInst *MI, unsigned Op, + raw_ostream &OS) { + OS << markup(""); + ; +} +void Z80InstPrinterCommon::printPtr(const MCInst *MI, unsigned Op, + raw_ostream &OS) { + OS << markup(""); +} +void Z80InstPrinterCommon::printOff(const MCInst *MI, unsigned Op, + raw_ostream &OS) { + OS << markup(""); +} +void Z80InstPrinterCommon::printAddr(const MCInst *MI, unsigned Op, + raw_ostream &OS) { + printOperand(MI, Op, OS); + auto Off = MI->getOperand(Op + 1).getImm(); + assert(isInt<8>(Off) && "Offset out of range!"); + OS << " + " << int(int8_t(Off)); +} +void Z80InstPrinterCommon::printBit(const MCInst *MI, unsigned Op, + raw_ostream &OS) { + auto Off = MI->getOperand(Op).getImm(); + assert(isUInt<3>(Off) && "Offset out of range!"); + OS << int(Off & 7); +} diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinterCommon.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinterCommon.h new file mode 100644 index 00000000000000..fbb67a9b21c121 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinterCommon.h @@ -0,0 +1,47 @@ +//===- Z80InstPrinterCommon.h - Convert Z80 MCInst to assembly --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file includes code common for rendering MCInst instances as (e)Z80 +// assembly. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_INSTPRINTER_Z80INSTPRINTERCOMMON_H +#define LLVM_LIB_TARGET_Z80_INSTPRINTER_Z80INSTPRINTERCOMMON_H + +#include "llvm/MC/MCInstPrinter.h" + +namespace llvm { + class Z80InstPrinterCommon : public MCInstPrinter { + public: + Z80InstPrinterCommon(const MCAsmInfo &MAI, const MCInstrInfo &MII, + const MCRegisterInfo &MRI) + : MCInstPrinter(MAI, MII, MRI) {} + + void printRegName(raw_ostream &OS, unsigned RegNo) const override; + void printInst(const MCInst *MI, uint64_t Address, StringRef Annot, + const MCSubtargetInfo &STI, raw_ostream &OS) override; + + // Autogenerated by tblgen. + virtual void printInstruction(const MCInst *MI, uint64_t Address, + raw_ostream &OS) = 0; + virtual StringRef getRegName(unsigned RegNo) const = 0; + + void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &OS); + void printCCOperand(const MCInst *MI, unsigned OpNo, raw_ostream &OS); + + void printMem(const MCInst *MI, unsigned OpNo, raw_ostream &OS); + void printPtr(const MCInst *MI, unsigned OpNo, raw_ostream &OS); + void printOff(const MCInst *MI, unsigned OpNo, raw_ostream &OS); + void printAddr(const MCInst *MI, unsigned OpNo, raw_ostream &OS); + void printBit(const MCInst *MI, unsigned OpNo, raw_ostream &OS); + }; +} + +#endif diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp new file mode 100644 index 00000000000000..f99ff291ede0c7 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp @@ -0,0 +1,57 @@ +//===-- Z80MCAsmInfo.cpp - Z80 asm properties -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the declarations of the Z80MCAsmInfo properties. +// +//===----------------------------------------------------------------------===// + +#include "Z80MCAsmInfo.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +using namespace llvm; + +void Z80MCAsmInfo::anchor() { } + +Z80MCAsmInfo::Z80MCAsmInfo(const Triple &T) { + bool Is16Bit = T.isArch16Bit() || T.getEnvironment() == Triple::CODE16; + CodePointerSize = CalleeSaveStackSlotSize = Is16Bit ? 2 : 3; + MaxInstLength = 6; + DollarIsPC = true; + SeparatorString = nullptr; + CommentString = ";"; + PrivateGlobalPrefix = PrivateLabelPrefix = ""; + Code16Directive = ".assume\tadl = 0"; + Code24Directive = ".assume\tadl = 1"; + Code32Directive = Code64Directive = nullptr; + AssemblerDialect = !Is16Bit; + SupportsQuotedNames = false; + ZeroDirective = AsciiDirective = AscizDirective = nullptr; + Data8bitsDirective = "\tDB\t"; + Data16bitsDirective = "\tDW\t"; + Data24bitsDirective = "\tDW24\t"; + Data32bitsDirective = "\tDL\t"; + Data64bitsDirective = nullptr; + GlobalDirective = "\tXDEF\t"; + HasFunctionAlignment = false; + HasDotTypeDotSizeDirective = false; + WeakDirective = nullptr; + UseIntegratedAssembler = false; + WeakDirective = nullptr; + UseLogicalShr = false; +} + +const char *Z80MCAsmInfo::getBlockDirective(int64_t Size) const { + switch (Size) { + default: return nullptr; + case 1: return "\tBLKB\t"; + case 2: return "\tBLKW\t"; + case 3: return "\tBLKP\t"; + case 4: return "\tBLKL\t"; + } +} diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.h new file mode 100644 index 00000000000000..1dba0512bed4b4 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.h @@ -0,0 +1,32 @@ +//===-- Z80MCAsmInfo.h - Z80 asm properties --------------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the declaration of the Z80MCAsmInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_MCTARGETDESC_Z80MCASMINFO_H +#define LLVM_LIB_TARGET_Z80_MCTARGETDESC_Z80MCASMINFO_H + +#include "llvm/MC/MCAsmInfoOMF.h" + +namespace llvm { +class Triple; + +class Z80MCAsmInfo : public MCAsmInfoOMF { + void anchor() override; + +public: + explicit Z80MCAsmInfo(const Triple &Triple); + + const char *getBlockDirective(int64_t Size) const override; +}; +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.cpp new file mode 100644 index 00000000000000..b31616295649ce --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.cpp @@ -0,0 +1,94 @@ +//===-- Z80MCTargetDesc.cpp - Z80 Target Descriptions ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides Z80 specific target descriptions. +// +//===----------------------------------------------------------------------===// + +#include "Z80MCTargetDesc.h" +#include "EZ80InstPrinter.h" +#include "Z80InstPrinter.h" +#include "Z80MCAsmInfo.h" +#include "Z80TargetStreamer.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/TargetRegistry.h" +using namespace llvm; + +#define GET_REGINFO_MC_DESC +#include "Z80GenRegisterInfo.inc" + +#define GET_INSTRINFO_MC_DESC +#include "Z80GenInstrInfo.inc" + +#define GET_SUBTARGETINFO_MC_DESC +#include "Z80GenSubtargetInfo.inc" + +std::string Z80_MC::ParseZ80Triple(const Triple &TT) { + std::string FS; + if (TT.getArch() == Triple::ez80) + FS = "+24bit-mode,-16bit-mode"; + else + FS = "-24bit-mode,+16bit-mode"; + + return FS; +} + +MCSubtargetInfo *Z80_MC::createZ80MCSubtargetInfo(const Triple &TT, + StringRef CPU, StringRef FS) { + std::string ArchFS = Z80_MC::ParseZ80Triple(TT); + if (!FS.empty()) { + if (!ArchFS.empty()) + ArchFS = (Twine(ArchFS) + "," + FS).str(); + else + ArchFS = FS.str(); + } + + return createZ80MCSubtargetInfoImpl(TT, CPU, ArchFS); +} + +static MCAsmInfo *createZ80MCAsmInfo(const MCRegisterInfo &MRI, + const Triple &TheTriple, + const MCTargetOptions &Options) { + return new Z80MCAsmInfo(TheTriple); +} + +static MCInstPrinter *createZ80MCInstPrinter(const Triple &T, + unsigned SyntaxVariant, + const MCAsmInfo &MAI, + const MCInstrInfo &MII, + const MCRegisterInfo &MRI) { + if (SyntaxVariant == 0) + return new Z80InstPrinter(MAI, MII, MRI); + if (SyntaxVariant == 1) + return new Z80EInstPrinter(MAI, MII, MRI); + return nullptr; +} + +static MCTargetStreamer * +createZ80AsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS, + MCInstPrinter */*InstPrint*/, + bool /*isVerboseAsm*/) { + return new Z80TargetAsmStreamer(S, OS); +} + +// Force static initialization. +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeZ80TargetMC() { + for (Target *T : {&getTheZ80Target(), &getTheEZ80Target()}) { + // Register the MC asm info. + RegisterMCAsmInfoFn X(*T, createZ80MCAsmInfo); + + // Register the MCInstPrinter. + TargetRegistry::RegisterMCInstPrinter(*T, createZ80MCInstPrinter); + + // Register the asm target streamer. + TargetRegistry::RegisterAsmTargetStreamer(*T, createZ80AsmTargetStreamer); + } +} diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.h new file mode 100644 index 00000000000000..6beaaf7f96b522 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.h @@ -0,0 +1,81 @@ +//===-- Z80MCTargetDesc.h - Z80 Target Descriptions -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides Z80 specific target descriptions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_MCTARGETDESC_Z80MCTARGETDESC_H +#define LLVM_LIB_TARGET_Z80_MCTARGETDESC_Z80MCTARGETDESC_H + +#include "llvm/Support/DataTypes.h" +#include +#include + +namespace llvm { +class MCAsmBackend; +class MCCodeEmitter; +class MCContext; +class MCInstrInfo; +class MCObjectWriter; +class MCRegisterInfo; +class MCSubtargetInfo; +class MCTargetOptions; +class Target; +class Triple; +class StringRef; +class raw_pwrite_stream; + +Target &getTheZ80Target(); +Target &getTheEZ80Target(); + +namespace Z80_MC { +std::string ParseZ80Triple(const Triple &TT); + +/// Create a Z80 MCSubtargetInfo instance. This is exposed so Asm parser, etc. +/// do not need to go through TargetRegistry. +MCSubtargetInfo *createZ80MCSubtargetInfo(const Triple &TT, StringRef CPU, + StringRef FS); +} + +MCCodeEmitter *createZ80MCCodeEmitter(const MCInstrInfo &MCII, + const MCRegisterInfo &MRI, + MCContext &Ctx); + +MCAsmBackend *createZ80AsmBackend(const Target &T, const MCRegisterInfo &MRI, + const Triple &TT, StringRef CPU, + const MCTargetOptions &Options); +MCAsmBackend *createEZ80AsmBackend(const Target &T, const MCRegisterInfo &MRI, + const Triple &TT, StringRef CPU, + const MCTargetOptions &Options); + +/// Construct a Z80 OMF object writer. +std::unique_ptr createZ80OMFObjectWriter(raw_pwrite_stream &OS); + +/// Construct a Z80 ELF object writer. +std::unique_ptr createZ80ELFObjectWriter(raw_pwrite_stream &OS, + uint8_t OSABI = 0); + +} // End llvm namespace + +// Defines symbolic names for Z80 registers. This defines a mapping from +// register name to register number. +// +#define GET_REGINFO_ENUM +#include "Z80GenRegisterInfo.inc" + +// Defines symbolic names for the Z80 instructions. +// +#define GET_INSTRINFO_ENUM +#include "Z80GenInstrInfo.inc" + +#define GET_SUBTARGETINFO_ENUM +#include "Z80GenSubtargetInfo.inc" + +#endif diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.cpp new file mode 100644 index 00000000000000..6bdcf98ea057ec --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.cpp @@ -0,0 +1,47 @@ +//===- Z80TargetStreamer.cpp - Z80TargetStreamer class --*- C++ -*---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Z80TargetStreamer class. +// +//===----------------------------------------------------------------------===// + +#include "Z80TargetStreamer.h" +#include "llvm/MC/MCContext.h" +#include "llvm/Support/FormattedStream.h" + +using namespace llvm; + +Z80TargetStreamer::Z80TargetStreamer(MCStreamer &S) + : MCTargetStreamer(S) {} + +Z80TargetAsmStreamer::Z80TargetAsmStreamer(MCStreamer &S, + formatted_raw_ostream &OS) + : Z80TargetStreamer(S), MAI(S.getContext().getAsmInfo()), OS(OS) {} + +void Z80TargetAsmStreamer::emitAlign(unsigned ByteAlignment) { + if (ByteAlignment > 1) + OS << "\tALIGN\t" << ByteAlignment << '\n'; +} + +void Z80TargetAsmStreamer::emitBlock(uint64_t NumBytes) { + if (NumBytes) + OS << "\tDS\t" << NumBytes << '\n'; +} + +void Z80TargetAsmStreamer::emitGlobal(MCSymbol *Symbol) { + OS << "\tXDEF\t"; + Symbol->print(OS, MAI); + OS << '\n'; +} + +void Z80TargetAsmStreamer::emitExtern(MCSymbol *Symbol) { + OS << "\tXREF\t"; + Symbol->print(OS, MAI); + OS << '\n'; +} diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.h new file mode 100644 index 00000000000000..af7b93c56efd65 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.h @@ -0,0 +1,55 @@ +//==-- Z80TargetStreamer.h - Z80 Target Streamer -----------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file declares Z80-specific target streamer classes. +/// These are for implementing support for target-specific assembly directives. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_MCTARGETDESC_Z80TARGETSTREAMER_H +#define LLVM_LIB_TARGET_Z80_MCTARGETDESC_Z80TARGETSTREAMER_H + +#include "llvm/MC/MCStreamer.h" + +namespace llvm { + +class Z80TargetStreamer : public MCTargetStreamer { +public: + explicit Z80TargetStreamer(MCStreamer &S); + + // .align + virtual void emitAlign(unsigned ByteAlignment) = 0; + + // .block + virtual void emitBlock(uint64_t NumBytes) = 0; + + // .global + virtual void emitGlobal(MCSymbol *Symbol) = 0; + + // .extern + virtual void emitExtern(MCSymbol *Symbol) = 0; +}; + +class Z80TargetAsmStreamer final : public Z80TargetStreamer { + const MCAsmInfo *MAI; + formatted_raw_ostream &OS; + +public: + Z80TargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS); + + void emitAlign(unsigned ByteAlignment) override; + void emitBlock(uint64_t NumBytes) override; + void emitGlobal(MCSymbol *Symbol) override; + void emitExtern(MCSymbol *Symbol) override; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_Z80_MCTARGETDESC_Z80TARGETSTREAMER_H diff --git a/llvm/lib/Target/Z80/TargetInfo/CMakeLists.txt b/llvm/lib/Target/Z80/TargetInfo/CMakeLists.txt new file mode 100644 index 00000000000000..f8c220752fb025 --- /dev/null +++ b/llvm/lib/Target/Z80/TargetInfo/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_component_library(LLVMZ80Info + Z80TargetInfo.cpp + ) diff --git a/llvm/lib/Target/Z80/TargetInfo/LLVMBuild.txt b/llvm/lib/Target/Z80/TargetInfo/LLVMBuild.txt new file mode 100644 index 00000000000000..3abdee8efa64fb --- /dev/null +++ b/llvm/lib/Target/Z80/TargetInfo/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./lib/Target/Z80/TargetInfo/LLVMBuild.txt ----------------*- Conf -*--===; +; +; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +; See https://llvm.org/LICENSE.txt for license information. +; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = Z80Info +parent = Z80 +required_libraries = Support +add_to_library_groups = Z80 diff --git a/llvm/lib/Target/Z80/TargetInfo/Z80TargetInfo.cpp b/llvm/lib/Target/Z80/TargetInfo/Z80TargetInfo.cpp new file mode 100644 index 00000000000000..38e4bf7d201b7a --- /dev/null +++ b/llvm/lib/Target/Z80/TargetInfo/Z80TargetInfo.cpp @@ -0,0 +1,28 @@ +//===-- Z80TargetInfo.cpp - Z80 Target Implementation ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "TargetInfo/Z80TargetInfo.h" +#include "llvm/Support/TargetRegistry.h" +using namespace llvm; + +Target &llvm::getTheZ80Target() { + static Target TheZ80Target; + return TheZ80Target; +} +Target &llvm::getTheEZ80Target() { + static Target TheEZ80Target; + return TheEZ80Target; +} + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeZ80TargetInfo() { + RegisterTarget X( + getTheZ80Target(), "z80", "Z80 [experimental]", "Z80"); + + RegisterTarget Y( + getTheEZ80Target(), "ez80", "eZ80 [experimental]", "EZ80"); +} diff --git a/llvm/lib/Target/Z80/TargetInfo/Z80TargetInfo.h b/llvm/lib/Target/Z80/TargetInfo/Z80TargetInfo.h new file mode 100644 index 00000000000000..6d3db110f460f1 --- /dev/null +++ b/llvm/lib/Target/Z80/TargetInfo/Z80TargetInfo.h @@ -0,0 +1,21 @@ +//===-- Z80TargetInfo.h - Z80 Target Implementation -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_TARGETINFO_Z80TARGETINFO_H +#define LLVM_LIB_TARGET_Z80_TARGETINFO_Z80TARGETINFO_H + +namespace llvm { + +class Target; + +Target &getTheZ80Target(); +Target &getTheEZ80Target(); + +} + +#endif // LLVM_LIB_TARGET_Z80_TARGETINFO_Z80TARGETINFO_H diff --git a/llvm/lib/Target/Z80/Z80.h b/llvm/lib/Target/Z80/Z80.h new file mode 100644 index 00000000000000..f2d731f2fc334e --- /dev/null +++ b/llvm/lib/Target/Z80/Z80.h @@ -0,0 +1,26 @@ +//===-- Z80.h - Top-level interface for Z80 representation ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the entry points for global functions defined in the LLVM +// Z80 back-end. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80_H +#define LLVM_LIB_TARGET_Z80_Z80_H + +#include "llvm/Support/CodeGen.h" + +namespace llvm { + +class FunctionPass; + +} // namespace llvm + +#endif diff --git a/llvm/lib/Target/Z80/Z80.td b/llvm/lib/Target/Z80/Z80.td new file mode 100644 index 00000000000000..666ee2416057f5 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80.td @@ -0,0 +1,92 @@ +//===-- Z80.td - Target definition file for the Zilog Z80 --*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a target description file for the Zilog z80 architecture, referred +// to here as the "Z80" architecture. +// +//===----------------------------------------------------------------------===// + +// Get the target-independent interfaces which we are implementing... +// +include "llvm/Target/Target.td" + +//===----------------------------------------------------------------------===// +// Z80 Subtarget features +//===----------------------------------------------------------------------===// + +def FeatureUndoc : SubtargetFeature<"undoc", "HasUndocOps", "true", + "Enable undocumented z80 instructions">; +def FeatureZ180 : SubtargetFeature<"z180", "HasZ180Ops", "true", + "Support z180 instructions">; +def FeatureEZ80 : SubtargetFeature<"ez80", "HasEZ80Ops", "true", + "Support ez80 instructions">; +def FeatureIdxHalf : SubtargetFeature<"idxhalf", "HasIdxHalfRegs", "true", + "Support index half registers">; + +//===----------------------------------------------------------------------===// +// Z80 Subtarget state +//===----------------------------------------------------------------------===// + +def Mode24Bit : SubtargetFeature<"24bit-mode", "In24BitMode", "true", + "24-bit mode (ez80)", [FeatureEZ80]>; +def Mode16Bit : SubtargetFeature<"16bit-mode", "In16BitMode", "true", + "16-bit mode (z80)">; + +//===----------------------------------------------------------------------===// +// Z80 processors supported +//===----------------------------------------------------------------------===// + +let CompleteModel = 0 in def GenericModel : SchedMachineModel; +class Proc Features> + : ProcessorModel; +def : Proc<"generic", []>; +def : Proc<"z80", [FeatureUndoc, FeatureIdxHalf]>; +def : Proc<"z180", [FeatureZ180]>; +def : Proc<"ez80", [FeatureZ180, FeatureEZ80, FeatureIdxHalf]>; + +//===----------------------------------------------------------------------===// +// Register File Description +//===----------------------------------------------------------------------===// + +include "Z80RegisterInfo.td" + +//===----------------------------------------------------------------------===// +// Instruction Descriptions +//===----------------------------------------------------------------------===// + +include "Z80InstrInfo.td" + +def Z80InstrInfo : InstrInfo; + +//===----------------------------------------------------------------------===// +// Calling Conventions +//===----------------------------------------------------------------------===// + +include "Z80CallingConv.td" + +//===----------------------------------------------------------------------===// +// Assembly writer +//===----------------------------------------------------------------------===// + +let ShouldEmitMatchRegisterName = 0 in { + def Z80AsmParser : AsmParser; + def EZ80AsmParser : AsmParser; +} +def Z80AsmWriter : AsmWriter; +def EZ80AsmWriter : AsmWriter { + string AsmWriterClassName = "EInstPrinter"; + let Variant = 1; +} + +def Z80 : Target { + // Information about the instructions... + let InstructionSet = Z80InstrInfo; + let AssemblyParsers = [Z80AsmParser, EZ80AsmParser]; + let AssemblyWriters = [Z80AsmWriter, EZ80AsmWriter]; +} diff --git a/llvm/lib/Target/Z80/Z80AsmPrinter.cpp b/llvm/lib/Target/Z80/Z80AsmPrinter.cpp new file mode 100644 index 00000000000000..3464183685722e --- /dev/null +++ b/llvm/lib/Target/Z80/Z80AsmPrinter.cpp @@ -0,0 +1,104 @@ +//===-- Z80AsmPrinter.cpp - Convert Z80 LLVM code to AT&T assembly --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a printer that converts from our internal representation +// of machine-dependent LLVM code to Z80 machine code. +// +//===----------------------------------------------------------------------===// + +#include "Z80AsmPrinter.h" +#include "Z80.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "MCTargetDesc/Z80TargetStreamer.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Target/TargetLoweringObjectFile.h" +#include "llvm/Target/TargetMachine.h" +using namespace llvm; + +//===----------------------------------------------------------------------===// +// Target Registry Stuff +//===----------------------------------------------------------------------===// + +static bool isCode16(const Triple &TT) { + return TT.getEnvironment() == Triple::CODE16; +} + +void Z80AsmPrinter::emitStartOfAsmFile(Module &M) { + const Triple &TT = TM.getTargetTriple(); + if (TT.getArch() == Triple::ez80) + OutStreamer->emitAssemblerFlag(isCode16(TT) ? MCAF_Code16 : MCAF_Code24); +} + +void Z80AsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo, + const MCSubtargetInfo *EndInfo) const { + bool Was16 = isCode16(StartInfo.getTargetTriple()); + if (!EndInfo || Was16 != isCode16(EndInfo->getTargetTriple())) + OutStreamer->emitAssemblerFlag(Was16 ? MCAF_Code16 : MCAF_Code24); +} + +void Z80AsmPrinter::emitEndOfAsmFile(Module &M) { + Z80TargetStreamer *TS = + static_cast(OutStreamer->getTargetStreamer()); + for (const auto &Symbol : OutContext.getSymbols()) + if (!Symbol.second->isDefined()) + TS->emitExtern(Symbol.second); +} + +void Z80AsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { + Z80TargetStreamer *TS = + static_cast(OutStreamer->getTargetStreamer()); + + if (GV->hasInitializer()) { + // Check to see if this is a special global used by LLVM, if so, emit it. + if (emitSpecialLLVMGlobal(GV)) + return; + } + + MCSymbol *GVSym = getSymbol(GV); + + if (!GV->hasInitializer()) // External globals require no extra code. + return; + + GVSym->redefineIfPossible(); + if (GVSym->isDefined() || GVSym->isVariable()) + report_fatal_error("symbol '" + Twine(GVSym->getName()) + + "' is already defined"); + + SectionKind GVKind = TargetLoweringObjectFile::getKindForGlobal(GV, TM); + + const DataLayout &DL = GV->getParent()->getDataLayout(); + uint64_t Size = DL.getTypeAllocSize(GV->getType()->getElementType()); + + // If the alignment is specified, we *must* obey it. Overaligning a global + // with a specified alignment is a prompt way to break globals emitted to + // sections and expected to be contiguous (e.g. ObjC metadata). + unsigned Align = DL.getPreferredAlignment(GV); + + // Determine to which section this global should be emitted. + MCSection *TheSection = getObjFileLowering().SectionForGlobal(GV, GVKind, TM); + + OutStreamer->SwitchSection(TheSection); + TS->emitAlign(Align); + if (!GV->hasLocalLinkage()) + TS->emitGlobal(GVSym); + OutStreamer->emitLabel(GVSym); + if (GVKind.isBSS()) + TS->emitBlock(Size); + else + emitGlobalConstant(DL, GV->getInitializer()); + OutStreamer->AddBlankLine(); +} + +// Force static initialization. +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeZ80AsmPrinter() { + RegisterAsmPrinter X(getTheZ80Target()); + RegisterAsmPrinter Y(getTheEZ80Target()); +} diff --git a/llvm/lib/Target/Z80/Z80AsmPrinter.h b/llvm/lib/Target/Z80/Z80AsmPrinter.h new file mode 100644 index 00000000000000..249f699aaabd73 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80AsmPrinter.h @@ -0,0 +1,42 @@ +//===-- Z80AsmPrinter.h - Z80 implementation of AsmPrinter ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80ASMPRINTER_H +#define LLVM_LIB_TARGET_Z80_Z80ASMPRINTER_H + +#include "Z80Subtarget.h" +#include "llvm/CodeGen/AsmPrinter.h" + +namespace llvm { + +class LLVM_LIBRARY_VISIBILITY Z80AsmPrinter : public AsmPrinter { + const Z80Subtarget *Subtarget; + +public: + explicit Z80AsmPrinter(TargetMachine &TM, + std::unique_ptr Streamer) + : AsmPrinter(TM, std::move(Streamer)) {} + + StringRef getPassName() const override { + return "Z80 Assembly / Object Emitter"; + } + + const Z80Subtarget &getSubtarget() const { return *Subtarget; } + + + void emitStartOfAsmFile(Module &M) override; + void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo, + const MCSubtargetInfo *EndInfo) const override; + void emitEndOfAsmFile(Module &M) override; + void emitGlobalVariable(const GlobalVariable *GV) override; + void emitInstruction(const MachineInstr *MI) override; +}; +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/Z80/Z80CallingConv.cpp b/llvm/lib/Target/Z80/Z80CallingConv.cpp new file mode 100644 index 00000000000000..94fb3f19f2fb5c --- /dev/null +++ b/llvm/lib/Target/Z80/Z80CallingConv.cpp @@ -0,0 +1,19 @@ +//=== Z80CallingConv.cpp - Z80 Custom Calling Convention Impl -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the implementation of custom routines for the Z80 +// Calling Convention that aren't done by tablegen. +// +//===----------------------------------------------------------------------===// + +#include "Z80CallingConv.h" +#include "Z80Subtarget.h" +using namespace llvm; + +// Provides entry points of CC_Z80 and RetCC_Z80. +#include "Z80GenCallingConv.inc" diff --git a/llvm/lib/Target/Z80/Z80CallingConv.h b/llvm/lib/Target/Z80/Z80CallingConv.h new file mode 100644 index 00000000000000..9c3e7670045ff8 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80CallingConv.h @@ -0,0 +1,32 @@ +//=== Z80CallingConv.h - Z80 Custom Calling Convention Routines -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the custom routines for the Z80 Calling Convention that +// aren't done by tablegen. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80CALLINGCONV_H +#define LLVM_LIB_TARGET_Z80_Z80CALLINGCONV_H + +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/IR/CallingConv.h" + +namespace llvm { + +bool RetCC_Z80(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, + CCState &State); + +bool CC_Z80(unsigned ValNo, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, + ISD::ArgFlagsTy ArgFlags, CCState &State); + +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/Z80/Z80CallingConv.td b/llvm/lib/Target/Z80/Z80CallingConv.td new file mode 100644 index 00000000000000..5030333562d8ef --- /dev/null +++ b/llvm/lib/Target/Z80/Z80CallingConv.td @@ -0,0 +1,126 @@ +//===-- Z80CallingConv.td - Calling Conventions Z80/EZ80 ---*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This describes the calling conventions for the Z80 and EZ80 architectures. +// +//===----------------------------------------------------------------------===// + +/// CCIfSubtarget - Match if the current subtarget has a feature F. +class CCIfSubtarget + : CCIf<"State.getMachineFunction().getSubtarget()." # F, A>; + +//===----------------------------------------------------------------------===// +// Z80 Register Calling Conventions +//===----------------------------------------------------------------------===// + +def RetCC_Z80_Reg : CallingConv<[ +//CCCustom<"CC_Z80_HandleSplit">, + CCIfType<[ i24 ], CCIfSplit>>, +//CCIfType<[ i24 ], CCIfSplitEnd>>, + CCIfType<[ i24 ], CCAssignToRegWithShadow<[ UHL, UDE, UBC, UIY ], + [ UDE, UHL, UIY, UBC ]>>, + CCIfType<[ i16 ], CCIfSplit>>, +//CCIfType<[ i16 ], CCIfSplitEnd>>, + CCIfType<[ i16 ], CCAssignToRegWithShadow<[ HL, DE, BC, IY ], + [ DE, HL, IY, BC ]>>, + CCIfType<[ i8 ], CCIfSplitEnd>> +]>; +def CC_Z80_Reg : CallingConv<[ + CCIfType<[ i64, f64 ], CCIfSubtarget<"is16Bit()", CCPassIndirect>>, + CCIfType<[ i64, f64 ], CCIfSubtarget<"is24Bit()", CCPassIndirect>>, + CCDelegateTo +]>; + +//===----------------------------------------------------------------------===// +// Z80 Argument Calling Conventions +//===----------------------------------------------------------------------===// + +def CC_Z80_C : CallingConv<[ + CCIfByVal>, + CCIfType<[ i1, i8 ], CCPromoteToType>, + CCIfType<[ i16 ], CCAssignToStack<2, 1>>, +]>; +def CC_EZ80_C : CallingConv<[ + CCIfByVal>, + CCIfType<[ i1, i8, i16 ], CCPromoteToType>, + CCIfType<[ i24 ], CCAssignToStack<3, 1>>, +]>; + +def CC_Z80_LC : CallingConv<[ + CCDelegateTo, + CCIfType<[ i8 ], CCAssignToReg<[ L, C ]>> +]>; +def CC_Z80_LC_AB : CallingConv<[ + CCIfType<[ i8 ], CCAssignToReg<[ A, B ]>> +]>; +def CC_Z80_LC_AC : CallingConv<[ + CCIfType<[ i8 ], CCAssignToReg<[ A, C ]>> +]>; +def CC_Z80_LC_BC : CallingConv<[ + CCIfType<[ i8 ], CCAssignToReg<[ B, C ]>> +]>; +def CC_Z80_LC_L : CallingConv<[ + CCIfType<[ i64, f64 ], CCIfSubtarget<"is16Bit()", CCPassIndirect>>, + CCIfType<[ i64, f64 ], CCIfSubtarget<"is24Bit()", CCPassIndirect>>, + CCIfType<[ i24 ], CCIfSplit>>, + CCIfType<[ i24 ], CCAssignToRegWithShadow<[ UBC, UIY, UHL, UDE ], + [ UIY, UBC, UDE, UHL ]>>, + CCIfType<[ i16 ], CCIfSplit>>, + CCIfType<[ i16 ], CCAssignToRegWithShadow<[ BC, IY, HL, DE ], + [ IY, BC, DE, HL ]>>, + CCIfType<[ i8 ], CCAssignToReg<[ C, A, L, E ]>> +]>; +def CC_Z80_LC_F : CallingConv<[ + CCDelegateTo, + CCIfType<[ i8 ], CCAssignToReg<[ F ]>> +]>; + +let Entry = 1 in +def CC_Z80 : CallingConv<[ + CCIfCC<"CallingConv::Z80_LibCall", CCDelegateTo>, + CCIfCC<"CallingConv::Z80_LibCall_AB", CCDelegateTo>, + CCIfCC<"CallingConv::Z80_LibCall_AC", CCDelegateTo>, + CCIfCC<"CallingConv::Z80_LibCall_BC", CCDelegateTo>, + CCIfCC<"CallingConv::Z80_LibCall_L", CCDelegateTo>, + CCIfCC<"CallingConv::Z80_LibCall_F", CCDelegateTo>, + CCIfSubtarget<"is16Bit()", CCDelegateTo>, + CCIfSubtarget<"is24Bit()", CCDelegateTo> +]>; + +//===----------------------------------------------------------------------===// +// Z80 Return Calling Conventions +//===----------------------------------------------------------------------===// +def RetCC_Z80_C : CallingConv<[ + CCIfType<[ i16 ], CCAssignToReg<[ HL, DE, BC, IY ]>>, + CCIfType<[ i8 ], CCAssignToReg<[ A, L, H, E, D, C, B, IYL, IYH ]>> +]>; +def RetCC_EZ80_C : CallingConv<[ + CCDelegateTo, + CCIfType<[ i8 ], CCAssignToReg<[ A ]>> +]>; + +// This is the return-value convention used for the entire Z80 backend. +let Entry = 1 in +def RetCC_Z80 : CallingConv<[ + CCIfType<[ i1 ], CCPromoteToType>, + CCIfCC<"CallingConv::Z80_LibCall_F", CCIfType<[ i8 ], CCAssignToReg<[ F ]>>>, + CCIfSubtarget<"is16Bit()", CCDelegateTo>, + CCIfCC<"CallingConv::Z80_LibCall_L", CCDelegateTo>, + CCIfSubtarget<"is24Bit()", CCDelegateTo> +]>; + +//===----------------------------------------------------------------------===// +// Callee-saved Registers. +//===----------------------------------------------------------------------===// + +def CSR_NoRegs : CalleeSavedRegs<(add)>; +def CSR_Z80_C : CalleeSavedRegs<(add IX)>; +def CSR_EZ80_C : CalleeSavedRegs<(add UIX)>; +def CSR_Z80_AllRegs : CalleeSavedRegs<(add R16, A)>; +def CSR_EZ80_AllRegs : CalleeSavedRegs<(add R24, A)>; diff --git a/llvm/lib/Target/Z80/Z80FrameLowering.cpp b/llvm/lib/Target/Z80/Z80FrameLowering.cpp new file mode 100644 index 00000000000000..7aae34bb3fb347 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80FrameLowering.cpp @@ -0,0 +1,418 @@ +//===-- Z80FrameLowering.cpp - Z80 Frame Information ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the z80 implementation of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#include "Z80FrameLowering.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "Z80.h" +#include "Z80InstrInfo.h" +#include "Z80MachineFunctionInfo.h" +#include "Z80Subtarget.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/Target/TargetMachine.h" +using namespace llvm; + +#define DEBUG_TYPE "z80-frame-lowering" + +Z80FrameLowering::Z80FrameLowering(const Z80Subtarget &STI) + : TargetFrameLowering(StackGrowsDown, Align(), STI.is24Bit() ? -3 : -2), + STI(STI), TII(*STI.getInstrInfo()), TRI(STI.getRegisterInfo()), + Is24Bit(STI.is24Bit()), SlotSize(Is24Bit ? 3 : 2) {} + +/// hasFP - Return true if the specified function should have a dedicated frame +/// pointer register. This is true if the function has variable sized allocas +/// or if frame pointer elimination is disabled. +bool Z80FrameLowering::hasFP(const MachineFunction &MF) const { + return MF.getTarget().Options.DisableFramePointerElim(MF) || + MF.getFrameInfo().hasStackObjects(); +} +bool Z80FrameLowering::hasReservedCallFrame(const MachineFunction &MF) const { + return false; // call frames are not implemented yet +} +bool Z80FrameLowering::needsFrameIndexResolution( + const MachineFunction &MF) const { + return TargetFrameLowering::needsFrameIndexResolution(MF) || + !hasReservedCallFrame(MF); +} + +Z80FrameLowering::StackAdjustmentMethod +Z80FrameLowering::getOptimalStackAdjustmentMethod(MachineFunction &MF, + int Offset, int FPOffset, + bool ScratchIsIndex, + bool UnknownOffset) const { + if (!Offset) + return SAM_None; + + if (MF.getFunction().hasOptNone()) + return SAM_Large; + + bool OptSize = MF.getFunction().hasOptSize(); + bool HasEZ80Ops = STI.hasEZ80Ops(); + + // Optimal for small offsets + // POP/PUSH for every SlotSize bytes + unsigned PopPushCount = std::abs(Offset) / SlotSize; + unsigned PopPushCost = OptSize ? 1 : HasEZ80Ops ? 4 : Offset > 0 ? 10 : 11; + if (ScratchIsIndex) + PopPushCost += OptSize || HasEZ80Ops ? 1 : 4; + // INC/DEC SP for remaining bytes + unsigned IncDecCount = std::abs(Offset) % SlotSize; + unsigned IncDecCost = OptSize || HasEZ80Ops ? 1 : 6; + + StackAdjustmentMethod BestMethod = PopPushCount ? SAM_Small : SAM_Tiny; + unsigned BestCost = PopPushCount * PopPushCost + IncDecCount * IncDecCost; + + if (UnknownOffset || (FPOffset >= 0 && FPOffset == Offset)) { + // Optimal if we are trying to set SP = FP, except for tiny Offset + unsigned AllCost = 0; + // LD SP, FP + AllCost += OptSize || HasEZ80Ops ? 2 : 10; + + return AllCost <= BestCost ? SAM_All : BestMethod; + } + + if (HasEZ80Ops && FPOffset >= 0 && isInt<8>(Offset - FPOffset) && hasFP(MF)) { + // Optimal for medium offsets + unsigned MediumCost = 0; + // LEA , FP - Offset - FPOffset + MediumCost += 3; + // LD SP, + MediumCost += ScratchIsIndex ? 2 : 1; + + if (MediumCost < BestCost) { + BestMethod = SAM_Medium; + BestCost = MediumCost; + } + } + + // Optimal for large offsets + unsigned LargeCost = 0; + // LD , Offset + LargeCost += OptSize || HasEZ80Ops ? 1 + SlotSize : 10; + // ADD , SP + LargeCost += OptSize || HasEZ80Ops ? 1 : 11; + // LD SP, + LargeCost += OptSize || HasEZ80Ops ? 1 : 6; + if (ScratchIsIndex) + LargeCost += OptSize || HasEZ80Ops ? 3 : 12; + + return LargeCost < BestCost ? SAM_Large : BestMethod; +} + +void Z80FrameLowering::BuildStackAdjustment(MachineFunction &MF, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + DebugLoc DL, Register ScratchReg, + int Offset, int FPOffset, + bool UnknownOffset) const { + Register FrameReg = TRI->getFrameRegister(MF), ResultReg; + bool ScratchIsIndex = Z80::I24RegClass.contains(ScratchReg) || + Z80::I16RegClass.contains(ScratchReg); + switch (getOptimalStackAdjustmentMethod(MF, Offset, FPOffset, ScratchIsIndex, + UnknownOffset)) { + case SAM_None: + // Nothing to do + return; + case SAM_Small: + for (unsigned PopPushCount = std::abs(Offset) / SlotSize; PopPushCount; + --PopPushCount) + BuildMI(MBB, MI, DL, + TII.get(Offset >= 0 ? (Is24Bit ? Z80::POP24r : Z80::POP16r) + : (Is24Bit ? Z80::PUSH24r : Z80::PUSH16r))) + .addReg(ScratchReg, getDefRegState(Offset >= 0) | + getDeadRegState(Offset >= 0) | + getUndefRegState(Offset < 0)); + LLVM_FALLTHROUGH; + case SAM_Tiny: + for (unsigned IncDecCount = std::abs(Offset) % SlotSize; IncDecCount; + --IncDecCount) + BuildMI(MBB, MI, DL, + TII.get(Offset >= 0 ? (Is24Bit ? Z80::INC24SP : Z80::INC16SP) + : (Is24Bit ? Z80::DEC24SP : Z80::DEC16SP))); + return; + case SAM_All: + // Just store FP to SP + ResultReg = FrameReg; + break; + case SAM_Medium: + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::LEA24ro : Z80::LEA16ro), + ScratchReg).addUse(FrameReg).addImm(Offset - FPOffset); + ResultReg = ScratchReg; + break; + case SAM_Large: + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::LD24ri : Z80::LD16ri), + ScratchReg).addImm(Offset); + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::ADD24SP : Z80::ADD16SP), + ScratchReg).addUse(ScratchReg)->addRegisterDead(Z80::F, TRI); + ResultReg = ScratchReg; + break; + } + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::LD24SP : Z80::LD16SP)) + .addUse(ResultReg, RegState::Kill); +} + +/// emitPrologue - Push callee-saved registers onto the stack, which +/// automatically adjust the stack pointer. Adjust the stack pointer to allocate +/// space for local variables. +void Z80FrameLowering::emitPrologue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + MachineBasicBlock::iterator MI = MBB.begin(); + + // Debug location must be unknown since the first debug location is used + // to determine the end of the prologue. + DebugLoc DL; + + MachineFrameInfo &MFI = MF.getFrameInfo(); + int StackSize = -int(MFI.getStackSize()); + MCRegister ScratchReg = Is24Bit ? Z80::UHL : Z80::HL; + + // skip callee-saved saves + while (MI != MBB.end() && MI->getFlag(MachineInstr::FrameSetup)) + ++MI; + + int FPOffset = -1; + if (hasFP(MF)) { + if (MF.getFunction().hasOptSize()) { + if (StackSize) { + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::LD24ri : Z80::LD16ri), + ScratchReg).addImm(StackSize); + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::CALL24 : Z80::CALL16)) + .addExternalSymbol("_frameset") + .addUse(ScratchReg, RegState::ImplicitKill); + return; + } + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::CALL24 : Z80::CALL16)) + .addExternalSymbol("_frameset0"); + return; + } + Register FrameReg = TRI->getFrameRegister(MF); + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addUse(FrameReg); + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::LD24ri : Z80::LD16ri), FrameReg) + .addImm(0); + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::ADD24SP : Z80::ADD16SP), + FrameReg).addUse(FrameReg)->addRegisterDead(Z80::F, TRI); + FPOffset = 0; + } + BuildStackAdjustment(MF, MBB, MI, DL, ScratchReg, StackSize, FPOffset); +} + +void Z80FrameLowering::emitEpilogue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + MachineBasicBlock::iterator MI = MBB.getFirstTerminator(); + DebugLoc DL = MBB.findDebugLoc(MI); + + MachineFrameInfo &MFI = MF.getFrameInfo(); + int StackSize = int(MFI.getStackSize()); + + const TargetRegisterClass *ScratchRC = Is24Bit ? &Z80::A24RegClass + : &Z80::A16RegClass; + TargetRegisterClass::iterator ScratchReg = ScratchRC->begin(); + for (; MI->readsRegister(TRI->getSubReg(*ScratchReg, Z80::sub_low), TRI); + ++ScratchReg) + assert(ScratchReg != ScratchRC->end() && + "Could not allocate a scratch register!"); + bool HasFP = hasFP(MF); + assert((HasFP || *ScratchReg != TRI->getFrameRegister(MF)) && + "Cannot allocate csr as scratch register!"); + + // skip callee-saved restores + while (MI != MBB.begin()) + if (!(--MI)->getFlag(MachineInstr::FrameDestroy)) { + ++MI; + break; + } + + // consume stack adjustment + while (MI != MBB.begin()) { + MachineBasicBlock::iterator PI = std::prev(MI); + unsigned Opc = PI->getOpcode(); + if ((Opc == Z80::POP24r || Opc == Z80::POP16r) && + PI->getOperand(0).isDead()) { + StackSize += SlotSize; + } else if (Opc == Z80::LD24SP || Opc == Z80::LD16SP) { + bool Is24Bit = Opc == Z80::LD24SP; + unsigned Reg = PI->getOperand(0).getReg(); + if (PI == MBB.begin()) + break; + MachineBasicBlock::iterator AI = std::prev(PI); + Opc = AI->getOpcode(); + if (AI == MBB.begin() || Opc != (Is24Bit ? Z80::ADD24SP : Z80::ADD16SP) || + AI->getOperand(0).getReg() != Reg || + AI->getOperand(1).getReg() != Reg) + break; + MachineBasicBlock::iterator LI = std::prev(AI); + Opc = LI->getOpcode(); + if (Opc != (Is24Bit ? Z80::LD24ri : Z80::LD16ri) || + LI->getOperand(0).getReg() != Reg) + break; + StackSize += LI->getOperand(1).getImm(); + LI->removeFromParent(); + AI->removeFromParent(); + } else + break; + PI->removeFromParent(); + } + + BuildStackAdjustment(MF, MBB, MI, DL, *ScratchReg, StackSize, + HasFP ? StackSize : -1, MFI.hasVarSizedObjects()); + if (HasFP) + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::POP24r : Z80::POP16r), + TRI->getFrameRegister(MF)); +} + +// Only non-nested non-nmi interrupts can use shadow registers. +static bool shouldUseShadow(const MachineFunction &MF) { + const Function &F = MF.getFunction(); + return F.getFnAttribute("interrupt").getValueAsString() == "Generic"; +} + +void Z80FrameLowering::shadowCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, DebugLoc DL, + MachineInstr::MIFlag Flag, const std::vector &CSI) const { + assert(shouldUseShadow(*MBB.getParent()) && + "Can't use shadow registers in this function."); + bool SaveAF = false, SaveG = false; + for (unsigned i = 0, e = CSI.size(); i != e; ++i) { + unsigned Reg = CSI[i].getReg(); + if (Reg == Z80::AF) + SaveAF = true; + else if (Z80::G24RegClass.contains(Reg) || + Z80::G16RegClass.contains(Reg)) + SaveG = true; + } + if (SaveAF) + BuildMI(MBB, MI, DL, TII.get(Z80::EXAF)) + .setMIFlag(Flag); + if (SaveG) + BuildMI(MBB, MI, DL, TII.get(Z80::EXX)) + .setMIFlag(Flag); +} + +bool Z80FrameLowering::assignCalleeSavedSpillSlots( + MachineFunction &MF, const TargetRegisterInfo *TRI, + std::vector &CSI) const { + MF.getInfo()->setCalleeSavedFrameSize( + (CSI.size() + hasFP(MF)) * SlotSize); + return true; +} + +bool Z80FrameLowering::spillCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + ArrayRef CSI, const TargetRegisterInfo *TRI) const { + const MachineFunction &MF = *MBB.getParent(); + const MachineRegisterInfo &MRI = MF.getRegInfo(); + bool UseShadow = shouldUseShadow(MF); + DebugLoc DL = MBB.findDebugLoc(MI); + if (UseShadow) + shadowCalleeSavedRegisters(MBB, MI, DL, MachineInstr::FrameSetup, CSI); + for (unsigned i = CSI.size(); i != 0; --i) { + unsigned Reg = CSI[i - 1].getReg(); + + // Non-index registers can be spilled to shadow registers. + if (UseShadow && !Z80::I24RegClass.contains(Reg) && + !Z80::I16RegClass.contains(Reg)) + continue; + + bool isLiveIn = MRI.isLiveIn(Reg); + if (!isLiveIn) + MBB.addLiveIn(Reg); + + // Decide whether we can add a kill flag to the use. + bool CanKill = !isLiveIn; + // Check if any subregister is live-in + if (CanKill) { + for (MCRegAliasIterator AReg(Reg, TRI, false); AReg.isValid(); ++AReg) { + if (MRI.isLiveIn(*AReg)) { + CanKill = false; + break; + } + } + } + + // Do not set a kill flag on values that are also marked as live-in. This + // happens with the @llvm-returnaddress intrinsic and with arguments + // passed in callee saved registers. + // Omitting the kill flags is conservatively correct even if the live-in + // is not used after all. + MachineInstrBuilder MIB; + if (Reg == Z80::AF) + MIB = BuildMI(MBB, MI, DL, + TII.get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)); + else + MIB = BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addUse(Reg, getKillRegState(CanKill)); + MIB.setMIFlag(MachineInstr::FrameSetup); + } + return true; +} +bool Z80FrameLowering::restoreCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + MutableArrayRef CSI, const TargetRegisterInfo *TRI) const { + const MachineFunction &MF = *MBB.getParent(); + bool UseShadow = shouldUseShadow(MF); + DebugLoc DL = MBB.findDebugLoc(MI); + for (unsigned i = 0, e = CSI.size(); i != e; ++i) { + unsigned Reg = CSI[i].getReg(); + + // Non-index registers can be spilled to shadow registers. + if (UseShadow && !Z80::I24RegClass.contains(Reg) && + !Z80::I16RegClass.contains(Reg)) + continue; + + MachineInstrBuilder MIB; + if (Reg == Z80::AF) + MIB = BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::POP24AF + : Z80::POP16AF)); + else + MIB = BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::POP24r : Z80::POP16r), + Reg); + MIB.setMIFlag(MachineInstr::FrameDestroy); + } + if (UseShadow) + shadowCalleeSavedRegisters(MBB, MI, DL, MachineInstr::FrameDestroy, CSI); + return true; +} + +void Z80FrameLowering::processFunctionBeforeFrameFinalized( + MachineFunction &MF, RegScavenger *RS) const { + MachineFrameInfo &MFI = MF.getFrameInfo(); + MFI.setMaxCallFrameSize(0); // call frames are not implemented yet + if (MF.getInfo()->getHasIllegalLEA() || + MFI.estimateStackSize(MF) > 0x80 - 2) { + int64_t MinFixedObjOffset = -int64_t(SlotSize); + for (int I = MFI.getObjectIndexBegin(); I < 0; ++I) + MinFixedObjOffset = std::min(MinFixedObjOffset, MFI.getObjectOffset(I)); + int FI = MFI.CreateFixedSpillStackObject( + SlotSize, MinFixedObjOffset - SlotSize * (1 + hasFP(MF))); + RS->addScavengingFrameIndex(FI); + } +} + +MachineBasicBlock::iterator Z80FrameLowering::eliminateCallFramePseudoInstr( + MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + if (!hasReservedCallFrame(MF)) { + if (int Amount = TII.getFrameAdjustment(*I)) { + Register ScratchReg = I->getOperand(I->getNumOperands() - 1).getReg(); + assert(ScratchReg.isPhysical() && + "Reg alloc should have already happened."); + BuildStackAdjustment(MF, MBB, I, I->getDebugLoc(), ScratchReg, Amount); + } + } + + return MBB.erase(I); +} diff --git a/llvm/lib/Target/Z80/Z80FrameLowering.h b/llvm/lib/Target/Z80/Z80FrameLowering.h new file mode 100644 index 00000000000000..8b61238a8fe8c9 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80FrameLowering.h @@ -0,0 +1,92 @@ +//===-- Z80TargetFrameLowering.h - Define frame lowering for Z80 -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements z80-specific bits of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80FRAMELOWERING_H +#define LLVM_LIB_TARGET_Z80_Z80FRAMELOWERING_H + +#include "llvm/CodeGen/TargetFrameLowering.h" + +namespace llvm { +class Z80Subtarget; +class Z80InstrInfo; +class Z80RegisterInfo; + +class Z80FrameLowering : public TargetFrameLowering { + const Z80Subtarget &STI; + const Z80InstrInfo &TII; + const Z80RegisterInfo *TRI; + + bool Is24Bit; + unsigned SlotSize; + +public: + explicit Z80FrameLowering(const Z80Subtarget &STI); + + /// Z80 has no alignment requirements. + bool targetHandlesStackFrameRounding() const override { return true; } + + /// emitProlog/emitEpilog - These methods insert prolog and epilog code into + /// the function. + void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + + bool + assignCalleeSavedSpillSlots(MachineFunction &MF, + const TargetRegisterInfo *TRI, + std::vector &CSI) const override; + bool spillCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + ArrayRef CSI, + const TargetRegisterInfo *TRI) const override; + bool + restoreCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + MutableArrayRef CSI, + const TargetRegisterInfo *TRI) const override; + + void processFunctionBeforeFrameFinalized( + MachineFunction &MF, RegScavenger *RS = nullptr) const override; + + MachineBasicBlock::iterator + eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI) const override; + + bool hasFP(const MachineFunction &MF) const override; + bool hasReservedCallFrame(const MachineFunction &MF) const override; + bool needsFrameIndexResolution(const MachineFunction &MF) const override; + + enum StackAdjustmentMethod { + SAM_None, + SAM_Tiny, + SAM_All, + SAM_Small, + SAM_Medium, + SAM_Large + }; + StackAdjustmentMethod getOptimalStackAdjustmentMethod( + MachineFunction &MF, int Offset, int FPOffset = -1, + bool ScratchIsIndex = false, bool UnknownOffset = false) const; + +private: + void BuildStackAdjustment(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, DebugLoc DL, + Register ScratchReg, int Offset, int FPOffset = -1, + bool UnknownOffset = false) const; + + void shadowCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, DebugLoc DL, + MachineInstr::MIFlag Flag, const std::vector &CSI) const; +}; +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/Z80/Z80ISelLowering.cpp b/llvm/lib/Target/Z80/Z80ISelLowering.cpp new file mode 100644 index 00000000000000..7d844a4f5349e6 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80ISelLowering.cpp @@ -0,0 +1,534 @@ +//===-- Z80ISelLowering.cpp - Z80 DAG Lowering Implementation -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interfaces that Z80 uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#include "Z80ISelLowering.h" +#include "Z80TargetMachine.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +using namespace llvm; + +#define DEBUG_TYPE "z80-isel" + +void Z80TargetLowering::setLibcall(RTLIB::Libcall Call, const char *Name, + CallingConv::ID CC) { + setLibcallName(Call, Name); + setLibcallCallingConv(Call, CC); +} + +Z80TargetLowering::Z80TargetLowering(const Z80TargetMachine &TM, + const Z80Subtarget &STI) + : TargetLowering(TM), Subtarget(STI) { + bool Is24Bit = Subtarget.is24Bit(); + + setSchedulingPreference(Sched::RegPressure); + + // Set up the register classes. + addRegisterClass(MVT::i8, &Z80::R8RegClass); + addRegisterClass(MVT::i16, &Z80::R16RegClass); + if (Is24Bit) + addRegisterClass(MVT::i24, &Z80::R24RegClass); + + setStackPointerRegisterToSaveRestore(Is24Bit ? Z80::SPL : Z80::SPS); + + // Compute derived properties from the register classes + computeRegisterProperties(STI.getRegisterInfo()); + + setLibcall(RTLIB::ZEXT_I16_I24, "_stoiu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SEXT_I16_I24, "_stoi", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SEXT_I24_I32, "_itol", CallingConv::Z80_LibCall ); + + setLibcall(RTLIB::NOT_I16, "_snot", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::NOT_I24, "_inot", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::NOT_I32, "_lnot", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::NOT_I64, "_llnot", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::AND_I16, "_sand", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::AND_I24, "_iand", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::AND_I32, "_land", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::AND_I64, "_lland", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::OR_I16, "_sor", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::OR_I24, "_ior", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::OR_I32, "_lor", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::OR_I64, "_llor", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::XOR_I16, "_sxor", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::XOR_I24, "_ixor", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::XOR_I32, "_lxor", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::XOR_I64, "_llxor", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SHL_I8, "_bshl", CallingConv::Z80_LibCall_AB); + setLibcall(RTLIB::SHL_I16, "_sshl", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SHL_I16_I8, "_sshl_b", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::SHL_I24, "_ishl", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SHL_I24_I8, "_ishl_b", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::SHL_I32, "_lshl", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::SHL_I64, "_llshl", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SRA_I8, "_bshrs", CallingConv::Z80_LibCall_AB); + setLibcall(RTLIB::SRA_I16, "_sshrs", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SRA_I16_I8, "_sshrs_b", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::SRA_I24, "_ishrs", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SRA_I24_I8, "_ishrs_b", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::SRA_I32, "_lshrs", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::SRA_I64, "_llshrs", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SRL_I8, "_bshru", CallingConv::Z80_LibCall_AB); + setLibcall(RTLIB::SRL_I16, "_sshru", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SRL_I16_I8, "_sshru_b", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::SRL_I24, "_ishru", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SRL_I24_I8, "_ishru_b", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::SRL_I32, "_lshru", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::SRL_I64, "_llshru", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::CMP_I32, "_lcmpu", CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::CMP_I64, "_llcmpu", CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::CMP_I16_0, "_scmpzero", CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::CMP_I24_0, "_icmpzero", CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::CMP_I32_0, "_lcmpzero", CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::CMP_I64_0, "_llcmpzero",CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::SCMP, "_setflag", CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::NEG_I16, "_sneg", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::NEG_I24, "_ineg", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::NEG_I32, "_lneg", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::NEG_I64, "_llneg", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::ADD_I32, "_ladd", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::ADD_I32_I8, "_ladd_b", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::ADD_I64, "_lladd", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SUB_I32, "_lsub", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SUB_I64, "_llsub", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::MUL_I8, "_bmulu", CallingConv::Z80_LibCall_BC); + setLibcall(RTLIB::MUL_I16, "_smulu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::MUL_I24, "_imulu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::MUL_I24_I8, "_imul_b", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::MUL_I32, "_lmulu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::MUL_I64, "_llmulu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SDIV_I8, "_bdivs", CallingConv::Z80_LibCall_BC); + setLibcall(RTLIB::SDIV_I16, "_sdivs", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SDIV_I24, "_idivs", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SDIV_I32, "_ldivs", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SDIV_I64, "_lldivs", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UDIV_I8, "_bdivu", CallingConv::Z80_LibCall_BC); + setLibcall(RTLIB::UDIV_I16, "_sdivu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UDIV_I24, "_idivu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UDIV_I32, "_ldivu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UDIV_I64, "_lldivu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SREM_I8, "_brems", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::SREM_I16, "_srems", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SREM_I24, "_irems", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SREM_I32, "_lrems", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SREM_I64, "_llrems", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UREM_I8, "_bremu", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::UREM_I16, "_sremu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UREM_I24, "_iremu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UREM_I32, "_lremu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UREM_I64, "_llremu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UDIVREM_I24, "_idvrmu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UDIVREM_I32, "_ldvrmu", CallingConv::Z80_LibCall ); + + setLibcall(RTLIB::POPCNT_I8, "_bpopcnt", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::POPCNT_I16, "_spopcnt", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::POPCNT_I24, "_ipopcnt", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::POPCNT_I32, "_lpopcnt", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::POPCNT_I64, "_llpopcnt", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::BITREV_I8, "_bbitrev", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::BITREV_I16, "_sbitrev", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::BITREV_I24, "_ibitrev", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::BITREV_I32, "_lbitrev", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::BITREV_I64, "_llbitrev", CallingConv::Z80_LibCall ); + + setLibcall(RTLIB::ADD_F32, "_fadd", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::SUB_F32, "_fsub", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::MUL_F32, "_fmul", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::DIV_F32, "_fdiv", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::REM_F32, "_frem", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::NEG_F32, "_fneg", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::CMP_F32, "_fcmp", CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::FPTOSINT_F32_I32, "_ftol", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::FPTOUINT_F32_I32, "_ftol", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::SINTTOFP_I32_F32, "_ltof", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::UINTTOFP_I32_F32, "_ultof", CallingConv::Z80_LibCall_L ); + + setLibcall(RTLIB::ADD_F64, "_dadd", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SUB_F64, "_dsub", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::MUL_F64, "_dmul", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::DIV_F64, "_ddiv", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::REM_F64, "_drem", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::NEG_F64, "_dneg", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::CMP_F64, "_dcmp", CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::FPEXT_F32_F64, "_ftod", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::FPROUND_F64_F32, "_dtof", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::FPTOSINT_F64_I32, "_dtol", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::FPTOUINT_F64_I32, "_dtoul", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SINTTOFP_I32_F64, "_ltod", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UINTTOFP_I32_F64, "_ultod", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::FPTOSINT_F64_I64, "_dtoll", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::FPTOUINT_F64_I64, "_dtoll", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SINTTOFP_I64_F64, "_lltod", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UINTTOFP_I64_F64, "_ulltod", CallingConv::Z80_LibCall ); +} + +unsigned Z80TargetLowering::getJumpTableEncoding() const { + return MachineJumpTableInfo::EK_BlockAddress; +} + +/// Return true if the target has native support for the specified value type +/// and it is 'desirable' to use the type for the given node type. e.g. On ez80 +/// i16 is legal, but undesirable since i16 instruction encodings are longer and +/// slower. +bool Z80TargetLowering::isTypeDesirableForOp(unsigned Opc, EVT VT) const { + if (!isTypeLegal(VT)) + return false; + if (Subtarget.is16Bit()) + return true; + + switch (Opc) { + default: + case ISD::SIGN_EXTEND: + case ISD::ZERO_EXTEND: + case ISD::ANY_EXTEND: + return true; + case ISD::LOAD: + case ISD::STORE: + case ISD::ADD: + case ISD::SUB: + return VT != MVT::i16; + case ISD::MUL: + case ISD::AND: + case ISD::OR: + case ISD::XOR: + case ISD::SHL: + case ISD::SRA: + case ISD::SRL: + return VT != MVT::i24; + } +} + +/// Return true if x op y -> (SrcVT)((DstVT)x op (DstVT)y) is beneficial. +bool Z80TargetLowering::isDesirableToShrinkOp(unsigned Opc, EVT SrcVT, + EVT DstVT) const { + if (!isTypeLegal(DstVT)) + return false; + switch (Opc) { + default: + return false; + case ISD::ADD: + case ISD::SUB: + case ISD::ADDC: + case ISD::SUBC: + case ISD::ADDE: + case ISD::SUBE: + // These require a .sis suffix for i24 -> i16 + return DstVT != MVT::i16 || Subtarget.is16Bit(); + case ISD::MUL: + case ISD::AND: + case ISD::OR: + case ISD::XOR: + case ISD::SHL: + case ISD::SRA: + case ISD::SRL: + case ISD::ROTL: + case ISD::ROTR: + // These are more expensive on larger types, so always shrink. + return true; + } +} + +/// This method query the target whether it is beneficial for dag combiner to +/// promote the specified node. If true, it should return the desired promotion +/// type by reference. +bool Z80TargetLowering::IsDesirableToPromoteOp(SDValue Op, EVT &PVT) const { + if (isDesirableToShrinkOp(Op.getOpcode(), MVT::i24, Op.getValueType())) + return false; + PVT = MVT::i24; + return true; +} + +MachineBasicBlock * +Z80TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) const { + switch (MI.getOpcode()) { + default: llvm_unreachable("Unexpected instr type to insert"); + /*case Z80::Sub016: + case Z80::Sub024: + return EmitLoweredSub0(MI, BB); + case Z80::Sub16: + case Z80::Sub24: + return EmitLoweredSub(MI, BB); + case Z80::Cp16a0: + case Z80::Cp24a0: + return EmitLoweredCmp0(MI, BB); + case Z80::Cp16ao: + case Z80::Cp24ao: + return EmitLoweredCmp(MI, BB);*/ + case Z80::Select8: + case Z80::Select16: + case Z80::Select24: + return EmitLoweredSelect(MI, BB); + case Z80::SExt8: + case Z80::SExt16: + case Z80::SExt24: + return EmitLoweredSExt(MI, BB); + case Z80::LDR16: + case Z80::LDR24: + return EmitLoweredMemMove(MI, BB); + } +} + +void Z80TargetLowering::AdjustInstrPostInstrSelection(MachineInstr &MI, + SDNode *Node) const { + switch (MI.getOpcode()) { + default: llvm_unreachable("Unexpected instr type to insert"); + case Z80::ADJCALLSTACKUP16: + case Z80::ADJCALLSTACKUP24: + case Z80::ADJCALLSTACKDOWN16: + case Z80::ADJCALLSTACKDOWN24: + return AdjustAdjCallStack(MI); + } +} + +void Z80TargetLowering::AdjustAdjCallStack(MachineInstr &MI) const { + bool Is24Bit = MI.getOpcode() == Z80::ADJCALLSTACKUP24 || + MI.getOpcode() == Z80::ADJCALLSTACKDOWN24; + assert((Is24Bit || MI.getOpcode() == Z80::ADJCALLSTACKUP16 || + MI.getOpcode() == Z80::ADJCALLSTACKDOWN16) && + "Unexpected opcode"); + MachineRegisterInfo &MRI = MI.getParent()->getParent()->getRegInfo(); + unsigned Reg = MRI.createVirtualRegister(Is24Bit ? &Z80::A24RegClass + : &Z80::A16RegClass); + MachineInstrBuilder(*MI.getParent()->getParent(), MI) + .addReg(Reg, RegState::ImplicitDefine | RegState::Dead); + LLVM_DEBUG(MI.dump()); +} + +MachineBasicBlock * +Z80TargetLowering::EmitLoweredSub(MachineInstr &MI, + MachineBasicBlock *BB) const { + bool Is24Bit = MI.getOpcode() == Z80::SUB24ao; + assert((Is24Bit || MI.getOpcode() == Z80::SUB16ao) && "Unexpected opcode"); + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + LLVM_DEBUG(BB->dump()); + BuildMI(*BB, MI, DL, TII->get(Z80::RCF)); + BuildMI(*BB, MI, DL, TII->get(Is24Bit ? Z80::SBC24ao : Z80::SBC16ao)) + .addReg(MI.getOperand(0).getReg()); + MI.eraseFromParent(); + LLVM_DEBUG(BB->dump()); + return BB; +} + +MachineBasicBlock * +Z80TargetLowering::EmitLoweredCmp(MachineInstr &MI, + MachineBasicBlock *BB) const { + bool Is24Bit = MI.getOpcode() == Z80::CP24ao; + assert((Is24Bit || MI.getOpcode() == Z80::CP16ao) && "Unexpected opcode"); + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + BuildMI(*BB, MI, DL, TII->get(Z80::RCF)); + BuildMI(*BB, MI, DL, TII->get(Is24Bit ? Z80::SBC24ao : Z80::SBC16ao)) + .addReg(MI.getOperand(0).getReg()); + BuildMI(*BB, MI, DL, TII->get(Is24Bit ? Z80::ADD24ao : Z80::ADD16ao), + Is24Bit ? Z80::UHL : Z80::HL).addReg(Is24Bit ? Z80::UHL : Z80::HL) + .addReg(MI.getOperand(0).getReg()); + MI.eraseFromParent(); + LLVM_DEBUG(BB->dump()); + return BB; +} + +MachineBasicBlock * +Z80TargetLowering::EmitLoweredCmp0(MachineInstr &MI, + MachineBasicBlock *BB) const { + bool Is24Bit = MI.getOpcode() == Z80::CP24a0; + assert((Is24Bit || MI.getOpcode() == Z80::CP16a0) && "Unexpected opcode"); + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + BuildMI(*BB, MI, DL, TII->get(Is24Bit ? Z80::ADD24ao : Z80::ADD16ao), + Is24Bit ? Z80::UHL : Z80::HL).addReg(Is24Bit ? Z80::UHL : Z80::HL) + .addReg(MI.getOperand(0).getReg()); + BuildMI(*BB, MI, DL, TII->get(Z80::RCF)); + BuildMI(*BB, MI, DL, TII->get(Is24Bit ? Z80::SBC24ao : Z80::SBC16ao)) + .addReg(MI.getOperand(0).getReg()); + MI.eraseFromParent(); + LLVM_DEBUG(BB->dump()); + return BB; +} + +MachineBasicBlock * +Z80TargetLowering::EmitLoweredSelect(MachineInstr &MI, + MachineBasicBlock *BB) const { + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + + // To "insert" a SELECT_CC instruction, we actually have to insert the + // diamond control-flow pattern. The incoming instruction knows the + // destination vreg to set, the condition code register to branch on, the + // true/false values to select between, and a branch opcode to use. + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction::iterator I = ++BB->getIterator(); + + // thisMBB: + // ... + // %FalseVal = ... + // cmpTY ccX, r1, r2 + // bCC copy1MBB + // fallthrough --> copy0MBB + MachineBasicBlock *thisMBB = BB; + MachineFunction *F = BB->getParent(); + MachineBasicBlock *copy0MBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *copy1MBB = F->CreateMachineBasicBlock(LLVM_BB); + F->insert(I, copy0MBB); + F->insert(I, copy1MBB); + + // Update machine-CFG edges by transferring all successors of the current + // block to the new block which will contain the Phi node for the select. + copy1MBB->splice(copy1MBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + copy1MBB->transferSuccessorsAndUpdatePHIs(BB); + // Next, add the true and fallthrough blocks as its successors. + BB->addSuccessor(copy0MBB); + BB->addSuccessor(copy1MBB); + + BuildMI(BB, DL, TII->get(Z80::JQCC)).addMBB(copy1MBB) + .addImm(MI.getOperand(3).getImm()); + + // copy0MBB: + // %TrueVal = ... + // # fallthrough to copy1MBB + BB = copy0MBB; + + // Update machine-CFG edges + BB->addSuccessor(copy1MBB); + + // copy1MBB: + // %Result = phi [ %FalseValue, copy0MBB ], [ %TrueValue, thisMBB ] + // ... + BB = copy1MBB; + BuildMI(*BB, BB->begin(), DL, TII->get(Z80::PHI), + MI.getOperand(0).getReg()) + .addReg(MI.getOperand(1).getReg()).addMBB(thisMBB) + .addReg(MI.getOperand(2).getReg()).addMBB(copy0MBB); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + LLVM_DEBUG(F->dump()); + return BB; +} + +MachineBasicBlock *Z80TargetLowering::EmitLoweredSExt( + MachineInstr &MI, MachineBasicBlock *BB) const { + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + unsigned Opc, Reg; + switch (MI.getOpcode()) { + default: llvm_unreachable("Unexpected opcode"); + case Z80::SExt8: + Opc = Z80::SBC8ar; + Reg = Z80::A; + break; + case Z80::SExt16: + Opc = Z80::SBC16aa; + Reg = Z80::HL; + break; + case Z80::SExt24: + Opc = Z80::SBC24aa; + Reg = Z80::UHL; + break; + } + MachineInstrBuilder MIB = BuildMI(*BB, MI, DL, TII->get(Opc)); + MIB->findRegisterUseOperand(Reg)->setIsUndef(); + if (Reg == Z80::A) + MIB.addReg(Reg, RegState::Undef); + MI.eraseFromParent(); + return BB; +} + +MachineBasicBlock * +Z80TargetLowering::EmitLoweredMemMove(MachineInstr &MI, + MachineBasicBlock *BB) const { + bool Is24Bit = MI.getOpcode() == Z80::LDR24; + Register DE = Is24Bit ? Z80::UDE : Z80::DE; + Register HL = Is24Bit ? Z80::UHL : Z80::HL; + Register BC = Is24Bit ? Z80::UBC : Z80::BC; + Register PhysRegs[] = { DE, HL, BC }; + assert((Is24Bit || MI.getOpcode() == Z80::LDR16) && "Unexpected opcode"); + + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + + // A memmove needs to choose between a forward and backwards copy. + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction::iterator I = ++BB->getIterator(); + + MachineFunction *F = BB->getParent(); + MachineBasicBlock *LDIR_BB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *LDDR_BB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *NextBB = F->CreateMachineBasicBlock(LLVM_BB); + for (auto *MBB : {LDIR_BB, LDDR_BB}) { + F->insert(I, MBB); + for (auto LiveIn : {BC, DE, HL}) + MBB->addLiveIn(LiveIn); + } + F->insert(I, NextBB); + + // Update machine-CFG edges by transferring all successors of the current + // block to the new block. + NextBB->splice(NextBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + NextBB->transferSuccessorsAndUpdatePHIs(BB); + + // BB: + // JP C,LDDR_BB + // fallthrough --> LDIR_BB + BuildMI(BB, DL, TII->get(Z80::JQCC)).addMBB(LDDR_BB) + .addImm(Z80::COND_C); + // Next, add the LDIR and LDDR blocks as its successors. + BB->addSuccessor(LDIR_BB); + BB->addSuccessor(LDDR_BB); + + // LDIR_BB: + // LDIR + // JP NextBB + for (int I = 0; I != 3; ++I) + BuildMI(LDIR_BB, DL, TII->get(Z80::COPY), PhysRegs[I]) + .add(MI.getOperand(I)); + BuildMI(LDIR_BB, DL, TII->get(Is24Bit ? Z80::LDIR24 : Z80::LDIR16)); + BuildMI(LDIR_BB, DL, TII->get(Z80::JQ)).addMBB(NextBB); + // Update machine-CFG edges + LDIR_BB->addSuccessor(NextBB); + + // LDDR_BB: + // ADD HL,BC + // DEC HL + // EX DE,HL + // ADD HL,BC + // DEC HL + // EX DE,HL + // LDDR + // # Fallthrough to Next MBB + for (int I = 0; I != 3; ++I) + BuildMI(LDDR_BB, DL, TII->get(Z80::COPY), PhysRegs[I]) + .add(MI.getOperand(I)); + for (int I = 0; I != 2; ++I) { + BuildMI(LDDR_BB, DL, TII->get(Is24Bit ? Z80::ADD24ao : Z80::ADD16ao), HL) + .addUse(HL).addUse(BC); + BuildMI(LDDR_BB, DL, TII->get(Is24Bit ? Z80::DEC24r : Z80::DEC16r), HL) + .addUse(HL); + BuildMI(LDDR_BB, DL, TII->get(Is24Bit ? Z80::EX24DE : Z80::EX16DE)); + } + BuildMI(LDDR_BB, DL, TII->get(Is24Bit ? Z80::LDDR24 : Z80::LDDR16)); + LDDR_BB->addSuccessor(NextBB); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + LLVM_DEBUG(F->dump()); + return NextBB; +} + +/// HandleByVal - Target-specific cleanup for ByVal support. +void Z80TargetLowering::HandleByVal(CCState *State, unsigned &Size, + Align Alignment) const { + // Round up to a multiple of the stack slot size. + Size = alignTo(Size, Subtarget.is24Bit() ? 3 : 2); +} diff --git a/llvm/lib/Target/Z80/Z80ISelLowering.h b/llvm/lib/Target/Z80/Z80ISelLowering.h new file mode 100644 index 00000000000000..80a098224f88ab --- /dev/null +++ b/llvm/lib/Target/Z80/Z80ISelLowering.h @@ -0,0 +1,129 @@ +//===-- Z80ISelLowering.h - Z80 DAG Lowering Interface ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interfaces that Z80 uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80ISELLOWERING_H +#define LLVM_LIB_TARGET_Z80_Z80ISELLOWERING_H + +#include "llvm/CodeGen/TargetLowering.h" + +namespace llvm { +class Z80Subtarget; +class Z80TargetMachine; + +namespace Z80ISD { +// Z80 Specific DAG Nodes +enum NodeType : unsigned { + // Start the numbering where the builtin ops leave off. + FIRST_NUMBER = ISD::BUILTIN_OP_END, + + /// A wrapper node for TargetConstantPool, TargetExternalSymbol, and + /// TargetGlobalAddress. + Wrapper, + + /// Shift/Rotate + RLC, RRC, RL, RR, SLA, SRA, SRL, + + /// Arithmetic operation with flags results. + INC, DEC, ADD, ADC, SUB, SBC, AND, XOR, OR, + + /// Z80 compare and test + CP, TST, + + MLT, + + /// This produces an all zeros/ones value from an input carry (SBC r,r). + SEXT, + + /// This operation represents an abstract Z80 call instruction, which + /// includes a bunch of information. + CALL, + + /// Return with a flag operand. Operand 0 is the chain operand, operand + /// 1 is the number of bytes of stack to pop. + RET_FLAG, + + /// Return from interrupt. + RETN_FLAG, RETI_FLAG, + + /// Tail call return. + TC_RETURN, + + /// BRCOND - Z80 conditional branch. The first operand is the chain, the + /// second is the block to branch to if the condition is true, the third is + /// the condition, and the fourth is the flag operand. + BRCOND, + + /// SELECT - Z80 select - This selects between a true value and a false + /// value (ops #1 and #2) based on the condition in op #0 and flag in op #3. + SELECT, + + /// Stack operations + POP = ISD::FIRST_TARGET_MEMORY_OPCODE, PUSH +}; +} // end Z80ISD namespace + +//===----------------------------------------------------------------------===// +// Z80 Implementation of the TargetLowering interface +class Z80TargetLowering final : public TargetLowering { + /// Keep a reference to the Z80Subtarget around so that we can + /// make the right decision when generating code for different targets. + const Z80Subtarget &Subtarget; + + void setLibcall(RTLIB::Libcall Call, const char *Name, CallingConv::ID CC); + +public: + Z80TargetLowering(const Z80TargetMachine &TM, const Z80Subtarget &STI); + +private: + unsigned getJumpTableEncoding() const override; + + /// Return true if the target has native support for + /// the specified value type and it is 'desirable' to use the type for the + /// given node type. e.g. On ez80 i16 is legal, but undesirable since i16 + /// instruction encodings are longer and slower. + bool isTypeDesirableForOp(unsigned Opc, EVT VT) const override; + + /// Return true if x op y -> (SrcVT)((DstVT)x op (DstVT)y) is beneficial. + bool isDesirableToShrinkOp(unsigned Opc, EVT SrcVT, EVT DstVT) const; + + bool IsDesirableToPromoteOp(SDValue Op, EVT &PVT) const override; + + MachineBasicBlock * + EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) const override; + void AdjustInstrPostInstrSelection(MachineInstr &MI, + SDNode *Node) const override; + + void AdjustAdjCallStack(MachineInstr &MI) const; + MachineBasicBlock *EmitLoweredSub0(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock *EmitLoweredSub(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock *EmitLoweredCmp0(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock *EmitLoweredCmp(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock *EmitLoweredSelect(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock *EmitLoweredSExt(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock *EmitLoweredMemMove(MachineInstr &MI, + MachineBasicBlock *BB) const; + + /// HandleByVal - Target-specific cleanup for ByVal support. + void HandleByVal(CCState *, unsigned &, Align) const override; +}; +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/Z80/Z80InstrFormats.td b/llvm/lib/Target/Z80/Z80InstrFormats.td new file mode 100644 index 00000000000000..d48426e34af227 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80InstrFormats.td @@ -0,0 +1,174 @@ +//===-- Z80InstrFormats.td - Z80 Instruction Formats -------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Z80 Instruction Format Definitions. +// + +class Mode val> { + bits<2> Value = val; +} +def AnyMode : Mode<0>; +def CurMode : Mode<1>; +def Z80Mode : Mode<2>; +def EZ80Mode : Mode<3>; + +class Prefix val> { + bits<4> Value = val; +} +def NoPre : Prefix<0>; +def CBPre : Prefix<1>; +def DDPre : Prefix<2>; +def DDCBPre : Prefix<3>; +def EDPre : Prefix<4>; +def FDPre : Prefix<5>; +def FDCBPre : Prefix<6>; +def IdxPre : Prefix<7>; +def IdxCBPre : Prefix<8>; +foreach i = 0-7 in +def Idx#i#Pre : Prefix; + +class ImmType val> { + bits<2> Value = val; +} +def NoImm : ImmType<0>; +def Imm : ImmType<1>; +def Off : ImmType<2>; +def OffImm : ImmType<3>; + +class Z80Inst opcode, ImmType immediate, + dag outputs = (outs), dag inputs = (ins), list pattern = [], + string asm = "", string constraints = ""> + : Instruction { + let Namespace = "Z80"; + + bits<8> Opcode = opcode; + bit hasImm = immediate.Value{0}; + bit hasOff = immediate.Value{1}; + + let OutOperandList = outputs; + let InOperandList = inputs; + let Pattern = pattern; + let AsmString = asm; + let Constraints = constraints; + + let TSFlags{3-0} = prefix.Value; + let TSFlags{5-4} = mode.Value; + let TSFlags{7-6} = immediate.Value; + let TSFlags{15-8} = opcode; + + let isCodeGenOnly = 1; +} + +let isPseudo = 1 in +class Pseudo pattern = []> + : Z80Inst< AnyMode, NoPre, 0, NoImm, outs, ins, pattern, + !strconcat(mnem, args), con>; + +class Inst opcode, ImmType immediate, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Z80Inst< AnyMode, prefix, opcode, immediate, outputs, inputs, pattern, + !strconcat(mnemonic, arguments), constraints>; +class Inst8 opcode, ImmType immediate, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Z80Inst< CurMode, prefix, opcode, immediate, outputs, inputs, pattern, + !strconcat(mnemonic, arguments), constraints>; +class Inst16 opcode, ImmType immediate, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Z80Inst< Z80Mode, prefix, opcode, immediate, outputs, inputs, pattern, + !strconcat(mnemonic, "{|.sis}", arguments), constraints>; +class Inst24 opcode, ImmType immediate, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Z80Inst, + Requires<[HaveEZ80Ops]>; + +class I opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst ; +class Ii opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst ; +class Io opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst ; +class Ioi opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst ; + +class I8i opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst8 ; +class I8oi opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst8 ; + +class I16 opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst16; +class I16i opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst16; +class I16o opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst16; +class I16oi opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst16; + +class I24 opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst24; +class I24i opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst24; +class I24o opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst24; +class I24oi opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst24; + +class P pattern = []> + : Z80Inst { + let isPseudo = 1; +} diff --git a/llvm/lib/Target/Z80/Z80InstrInfo.cpp b/llvm/lib/Target/Z80/Z80InstrInfo.cpp new file mode 100644 index 00000000000000..995251aac4833f --- /dev/null +++ b/llvm/lib/Target/Z80/Z80InstrInfo.cpp @@ -0,0 +1,1471 @@ +//===-- Z80InstrInfo.cpp - Z80 Instruction Information --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Z80 implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#include "Z80InstrInfo.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "Z80.h" +#include "Z80Subtarget.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/Debug.h" +using namespace llvm; + +#define DEBUG_TYPE "z80-instr-info" + +#define GET_INSTRINFO_CTOR_DTOR +#include "Z80GenInstrInfo.inc" + +// Pin the vtable to this file. +void Z80InstrInfo::anchor() {} + +Z80InstrInfo::Z80InstrInfo(Z80Subtarget &STI) + : Z80GenInstrInfo((STI.is24Bit() ? Z80::ADJCALLSTACKDOWN24 + : Z80::ADJCALLSTACKDOWN16), + (STI.is24Bit() ? Z80::ADJCALLSTACKUP24 + : Z80::ADJCALLSTACKUP16)), + Subtarget(STI), RI(STI.getTargetTriple()) { +} + +int Z80InstrInfo::getSPAdjust(const MachineInstr &MI) const { + switch (MI.getOpcode()) { + case Z80::POP16r: + case Z80::POP16AF: + return 2; + case Z80::POP24r: + case Z80::POP24AF: + return 3; + case Z80::PUSH16r: + case Z80::PUSH16AF: + case Z80::PEA16o: + return -2; + case Z80::PUSH24r: + case Z80::PUSH24AF: + case Z80::PEA24o: + return -3; + } + return TargetInstrInfo::getSPAdjust(MI); +} + +static bool isIndex(const MachineOperand &MO, const MCRegisterInfo &RI) { + if (MO.isFI()) + return true; + if (MO.isReg()) + for (MCRegister IndexReg : Z80::I24RegClass) + if (RI.isSubRegisterEq(IndexReg, MO.getReg())) + return true; + return false; +} + +static bool hasIndex(const MachineInstr &MI, const MCRegisterInfo &RI) { + for (const MachineOperand &Op : MI.explicit_operands()) + if (isIndex(Op, RI)) + return true; + return false; +} + +unsigned Z80InstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { + auto TSFlags = MI.getDesc().TSFlags; + // 1 byte for opcode + unsigned Size = 1; + // 1 byte if we need a suffix + switch (TSFlags & Z80II::ModeMask) { + case Z80II::AnyMode: + case Z80II::CurMode: + break; + case Z80II::Z80Mode: + Size += Subtarget.is24Bit(); + break; + case Z80II::EZ80Mode: + Size += Subtarget.is16Bit(); + break; + } + // prefix byte(s) + unsigned Prefix = TSFlags & Z80II::PrefixMask; + if (TSFlags & Z80II::IndexedIndexPrefix) + Size += isIndex(MI.getOperand(Prefix >> Z80II::PrefixShift), + getRegisterInfo()); + else + switch (Prefix) { + case Z80II::NoPrefix: + break; + case Z80II::CBPrefix: + case Z80II::DDPrefix: + case Z80II::EDPrefix: + case Z80II::FDPrefix: + Size += 1; + break; + case Z80II::DDCBPrefix: + case Z80II::FDCBPrefix: + Size += 2; + break; + case Z80II::AnyIndexPrefix: + Size += hasIndex(MI, getRegisterInfo()); + break; + } + // immediate byte(s) + if (TSFlags & Z80II::HasImm) + switch (TSFlags & Z80II::ModeMask) { + case Z80II::AnyMode: + Size += 1; + break; + case Z80II::CurMode: + Size += Subtarget.is24Bit() ? 3 : 2; + break; + case Z80II::Z80Mode: + Size += 2; + break; + case Z80II::EZ80Mode: + Size += 3; + break; + } + // 1 byte if we need an offset + if (TSFlags & Z80II::HasOff) + Size += 1; + return Size; +} + +Z80::CondCode Z80::GetBranchConditionForPredicate(CmpInst::Predicate Pred, + bool &IsSigned, + bool &IsSwapped, + bool &IsConst) { + IsSigned = CmpInst::isSigned(Pred); + IsSwapped = IsConst = false; + switch (Pred) { + case CmpInst::FCMP_FALSE: + case CmpInst::FCMP_UNO: + IsConst = true; + return Z80::COND_C; + case CmpInst::FCMP_TRUE: + case CmpInst::FCMP_ORD: + IsConst = true; + return Z80::COND_NC; + case CmpInst::FCMP_OEQ: + case CmpInst::FCMP_UEQ: + case CmpInst::ICMP_EQ: + return Z80::COND_Z; + case CmpInst::FCMP_ONE: + case CmpInst::FCMP_UNE: + case CmpInst::ICMP_NE: + return Z80::COND_NZ; + case CmpInst::ICMP_UGT: + IsSwapped = true; + LLVM_FALLTHROUGH; + case CmpInst::ICMP_ULT: + return Z80::COND_C; + case CmpInst::ICMP_ULE: + IsSwapped = true; + LLVM_FALLTHROUGH; + case CmpInst::ICMP_UGE: + return Z80::COND_NC; + case CmpInst::FCMP_OGT: + case CmpInst::FCMP_UGT: + case CmpInst::ICMP_SGT: + IsSwapped = true; + LLVM_FALLTHROUGH; + case CmpInst::FCMP_OLT: + case CmpInst::FCMP_ULT: + case CmpInst::ICMP_SLT: + return Z80::COND_M; + case CmpInst::FCMP_OLE: + case CmpInst::FCMP_ULE: + case CmpInst::ICMP_SLE: + IsSwapped = true; + LLVM_FALLTHROUGH; + case CmpInst::FCMP_OGE: + case CmpInst::FCMP_UGE: + case CmpInst::ICMP_SGE: + return Z80::COND_P; + default: + llvm_unreachable("Unknown predicate"); + } +} + +/// Return the inverse of the specified condition, +/// e.g. turning COND_E to COND_NE. +Z80::CondCode Z80::GetOppositeBranchCondition(Z80::CondCode CC) { + return Z80::CondCode(CC ^ 1); +} + +bool Z80InstrInfo::analyzeBranch(MachineBasicBlock &MBB, + MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const { + // Start from the bottom of the block and work up, examining the + // terminator instructions. + MachineBasicBlock::iterator I = MBB.end(), UnCondBrIter = I; + while (I != MBB.begin()) { + --I; + if (I->isDebugValue()) + continue; + + // Working from the bottom, when we see a non-terminator instruction, we're + // done. + if (!isUnpredicatedTerminator(*I)) + break; + + // A terminator that isn't a branch can't easily be handled by this + // analysis. + if (!I->isBranch()) + return true; + + // Cannot handle branches that don't branch to a block. + if (!I->getOperand(0).isMBB()) + return true; + + // Handle unconditional branches. + if (I->getNumOperands() == 1) { + UnCondBrIter = I; + + if (!AllowModify) { + TBB = I->getOperand(0).getMBB(); + continue; + } + + // If the block has any instructions after a JMP, delete them. + while (std::next(I) != MBB.end()) + std::next(I)->eraseFromParent(); + Cond.clear(); + FBB = nullptr; + + // Delete the JMP if it's equivalent to a fall-through. + if (MBB.isLayoutSuccessor(I->getOperand(0).getMBB())) { + TBB = nullptr; + I->eraseFromParent(); + I = MBB.end(); + UnCondBrIter = I; + continue; + } + + // TBB is used to indicate the unconditional destination. + TBB = I->getOperand(0).getMBB(); + continue; + } + + // Handle conditional branches. + assert(I->getNumExplicitOperands() == 2 && "Invalid conditional branch"); + Z80::CondCode BranchCode = Z80::CondCode(I->getOperand(1).getImm()); + + // Working from the bottom, handle the first conditional branch. + if (Cond.empty()) { + MachineBasicBlock *TargetBB = I->getOperand(0).getMBB(); + if (AllowModify && UnCondBrIter != MBB.end() && + MBB.isLayoutSuccessor(TargetBB)) { + // If we can modify the code and it ends in something like: + // + // jCC L1 + // jmp L2 + // L1: + // ... + // L2: + // + // Then we can change this to: + // + // jnCC L2 + // L1: + // ... + // L2: + // + // Which is a bit more efficient. + // We conditionally jump to the fall-through block. + BranchCode = GetOppositeBranchCondition(BranchCode); + MachineBasicBlock::iterator OldInst = I; + + BuildMI(MBB, UnCondBrIter, MBB.findDebugLoc(I), get(Z80::JQCC)) + .addMBB(UnCondBrIter->getOperand(0).getMBB()).addImm(BranchCode); + BuildMI(MBB, UnCondBrIter, MBB.findDebugLoc(I), get(Z80::JQ)) + .addMBB(TargetBB); + + OldInst->eraseFromParent(); + UnCondBrIter->eraseFromParent(); + + // Restart the analysis. + UnCondBrIter = MBB.end(); + I = MBB.end(); + continue; + } + + FBB = TBB; + TBB = I->getOperand(0).getMBB(); + Cond.push_back(MachineOperand::CreateImm(BranchCode)); + continue; + } + + return true; + } + + return false; +} + +unsigned Z80InstrInfo::removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved) const { + assert(!BytesRemoved && "code size not handled"); + MachineBasicBlock::iterator I = MBB.end(); + unsigned Count = 0; + + while (I != MBB.begin()) { + --I; + if (I->isDebugValue()) + continue; + if (I->getOpcode() != Z80::JQ && + I->getOpcode() != Z80::JQCC) + break; + // Remove the branch. + I->eraseFromParent(); + I = MBB.end(); + ++Count; + } + + return Count; +} + +unsigned Z80InstrInfo::insertBranch(MachineBasicBlock &MBB, + MachineBasicBlock *TBB, + MachineBasicBlock *FBB, + ArrayRef Cond, + const DebugLoc &DL, + int *BytesAdded) const { + // Shouldn't be a fall through. + assert(TBB && "InsertBranch must not be told to insert a fallthrough"); + assert(Cond.size() <= 1 && "Z80 branch conditions have one component!"); + assert(!BytesAdded && "code size not handled"); + + if (Cond.empty()) { + // Unconditional branch? + assert(!FBB && "Unconditional branch with multiple successors!"); + BuildMI(&MBB, DL, get(Z80::JQ)).addMBB(TBB); + return 1; + } + + // Conditional branch. + unsigned Count = 0; + BuildMI(&MBB, DL, get(Z80::JQCC)).addMBB(TBB).add(Cond[0]); + ++Count; + + // If FBB is null, it is implied to be a fall-through block. + if (FBB) { + // Two-way Conditional branch. Insert the second branch. + BuildMI(&MBB, DL, get(Z80::JQ)).addMBB(FBB); + ++Count; + } + return Count; +} + +bool Z80InstrInfo:: +reverseBranchCondition(SmallVectorImpl &Cond) const { + assert(Cond.size() == 1 && "Invalid Z80 branch condition!"); + Z80::CondCode CC = static_cast(Cond[0].getImm()); + Cond[0].setImm(GetOppositeBranchCondition(CC)); + return false; +} + +bool Z80::splitReg( + unsigned ByteSize, unsigned Opc8, unsigned Opc16, unsigned Opc24, + unsigned &RC, unsigned &LoOpc, unsigned &LoIdx, unsigned &HiOpc, + unsigned &HiIdx, unsigned &HiOff, bool Has16BitEZ80Ops) { + switch (ByteSize) { + default: llvm_unreachable("Unexpected Size!"); + case 1: + RC = Z80::R8RegClassID; + LoOpc = HiOpc = Opc8; + LoIdx = HiIdx = Z80::NoSubRegister; + HiOff = 0; + return false; + case 2: + if (Has16BitEZ80Ops) { + RC = Z80::R16RegClassID; + LoOpc = HiOpc = Opc16; + LoIdx = HiIdx = Z80::NoSubRegister; + HiOff = 0; + return false; + } + RC = Z80::R16RegClassID; + LoOpc = HiOpc = Opc8; + LoIdx = Z80::sub_low; + HiIdx = Z80::sub_high; + HiOff = 1; + return true; + case 3: + // Legalization should have taken care of this if we don't have eZ80 ops + //assert(Is24Bit && HasEZ80Ops && "Need eZ80 24-bit load/store"); + RC = Z80::R24RegClassID; + LoOpc = HiOpc = Opc24; + LoIdx = HiIdx = Z80::NoSubRegister; + HiOff = 0; + return false; + } +} + +bool Z80InstrInfo::canExchange(MCRegister RegA, MCRegister RegB) const { + // The only regs that can be directly exchanged are DE and HL, in any order. + bool DE = false, HL = false; + for (MCRegister Reg : {RegA, RegB}) { + if (RI.isSubRegisterEq(Z80::UDE, Reg)) + DE = true; + else if (RI.isSubRegisterEq(Z80::UHL, Reg)) + HL = true; + } + return DE && HL; +} + +void Z80InstrInfo::copyPhysReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const DebugLoc &DL, MCRegister DstReg, + MCRegister SrcReg, bool KillSrc) const { + LLVM_DEBUG(dbgs() << RI.getName(DstReg) << " = " << RI.getName(SrcReg) + << '\n'); + /*for (auto Regs : {std::make_pair(DstReg, &SrcReg), + std::make_pair(SrcReg, &DstReg)}) { + if (Z80::R8RegClass.contains(Regs.first) && + (Z80::R16RegClass.contains(*Regs.second) || + Z80::R24RegClass.contains(*Regs.second))) + *Regs.second = RI.getSubReg(*Regs.second, Z80::sub_low); + }*/ + // Identity copy. + if (DstReg == SrcReg) + return; + bool Is24Bit = Subtarget.is24Bit(); + if (Z80::R8RegClass.contains(DstReg, SrcReg)) { + // Byte copy. + if (Z80::G8RegClass.contains(DstReg, SrcReg)) { + // Neither are index registers. + BuildMI(MBB, MI, DL, get(Z80::LD8gg), DstReg) + .addReg(SrcReg, getKillRegState(KillSrc)); + } else if (Z80::I8RegClass.contains(DstReg, SrcReg)) { + assert(Subtarget.hasIndexHalfRegs() && "Need index half registers"); + // Both are index registers. + if (Z80::X8RegClass.contains(DstReg, SrcReg)) { + BuildMI(MBB, MI, DL, get(Z80::LD8xx), DstReg) + .addReg(SrcReg, getKillRegState(KillSrc)); + } else if (Z80::Y8RegClass.contains(DstReg, SrcReg)) { + BuildMI(MBB, MI, DL, get(Z80::LD8yy), DstReg) + .addReg(SrcReg, getKillRegState(KillSrc)); + } else { + // We are copying between different index registers, so we need to use + // an intermediate register. + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)) + ->findRegisterUseOperand(Z80::AF)->setIsUndef(); + BuildMI(MBB, MI, DL, get(Z80::X8RegClass.contains(SrcReg) ? Z80::LD8xx + : Z80::LD8yy), + Z80::A).addReg(SrcReg, getKillRegState(KillSrc)); + BuildMI(MBB, MI, DL, get(Z80::X8RegClass.contains(DstReg) ? Z80::LD8xx + : Z80::LD8yy), + DstReg).addReg(Z80::A); + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::POP24AF : Z80::POP16AF)); + } + } else { + assert(Subtarget.hasIndexHalfRegs() && "Need index half registers"); + // Only one is an index register, which isn't directly possible if one of + // them is from HL. If so, surround with EX DE,HL and use DE instead. + bool NeedEX = false; + for (MCRegister *Reg : {&DstReg, &SrcReg}) { + switch (*Reg) { + case Z80::H: *Reg = Z80::D; NeedEX = true; break; + case Z80::L: *Reg = Z80::E; NeedEX = true; break; + } + } + unsigned ExOpc = Is24Bit ? Z80::EX24DE : Z80::EX16DE; + if (NeedEX) { + // If the prev instr was an EX DE,HL, just kill it. + if (MI == MBB.begin() || std::prev(MI)->getOpcode() != ExOpc) { + auto Ex = BuildMI(MBB, MI, DL, get(ExOpc)); + Ex->findRegisterUseOperand(Is24Bit ? Z80::UDE : Z80::DE) + ->setIsUndef(); + Ex->findRegisterUseOperand(Is24Bit ? Z80::UHL : Z80::HL) + ->setIsUndef(); + } else + std::prev(MI)->eraseFromParent(); + } + BuildMI(MBB, MI, DL, + get(Z80::X8RegClass.contains(DstReg, SrcReg) ? Z80::LD8xx + : Z80::LD8yy), + DstReg).addReg(SrcReg, getKillRegState(KillSrc)); + if (NeedEX) + BuildMI(MBB, MI, DL, get(ExOpc)) + ->findRegisterUseOperand(Is24Bit ? Z80::UHL : Z80::HL)->setIsUndef(); + } + return; + } + // Specialized word copy. + if (DstReg == Z80::SPS || DstReg == Z80::SPL) { + // Copies to SP. + assert((Z80::A16RegClass.contains(SrcReg) || SrcReg == Z80::DE || + Z80::A24RegClass.contains(SrcReg) || SrcReg == Z80::UDE) && + "Unimplemented"); + Is24Bit |= DstReg == Z80::SPL; + MCRegister ExReg; + if (SrcReg == Z80::DE || SrcReg == Z80::UDE) { + ExReg = SrcReg; + SrcReg = SrcReg == Z80::DE ? Z80::HL : Z80::UHL; + } + if (ExReg) + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::EX24DE : Z80::EX16DE)) + .addReg(ExReg, RegState::ImplicitDefine) + .addReg(SrcReg, RegState::ImplicitKill); + BuildMI(MBB, MI, DL, get(DstReg == Z80::SPL ? Z80::LD24SP : Z80::LD16SP)) + .addReg(SrcReg, getKillRegState(KillSrc)); + if (ExReg) + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::EX24DE : Z80::EX16DE)) + .addReg(ExReg, RegState::ImplicitDefine) + .addReg(SrcReg, RegState::ImplicitDefine); + return; + } + if (SrcReg == Z80::SPL || SrcReg == Z80::SPS) { + // Copies from SP. + Is24Bit |= SrcReg == Z80::SPL; + MCRegister PopReg, ExReg; + if (DstReg == Z80::UBC || DstReg == Z80::BC) { + PopReg = DstReg; + DstReg = Is24Bit ? Z80::UHL : Z80::HL; + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(DstReg); + } else if (DstReg == Z80::UDE || DstReg == Z80::DE) { + ExReg = DstReg; + DstReg = Is24Bit ? Z80::UHL : Z80::HL; + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::EX24DE : Z80::EX16DE)) + .addReg(ExReg, RegState::ImplicitDefine) + .addReg(DstReg, RegState::ImplicitKill); + } + BuildMI(MBB, MI, DL, get(Subtarget.is24Bit() ? Z80::LD24ri : Z80::LD16ri), + DstReg).addImm(PopReg ? 2 + Is24Bit : 0); + BuildMI(MBB, MI, DL, get(SrcReg == Z80::SPL ? Z80::ADD24SP : Z80::ADD16SP), + DstReg).addReg(DstReg)->addRegisterDead(Z80::F, &getRegisterInfo()); + if (PopReg) { + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::EX24SP : Z80::EX16SP), DstReg) + .addReg(DstReg); + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::POP24r : Z80::POP16r), PopReg); + } else if (ExReg) + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::EX24DE : Z80::EX16DE)) + .addReg(ExReg, RegState::ImplicitDefine) + .addReg(DstReg, RegState::Implicit | getRegState(MI->getOperand(0))); + return; + } + Is24Bit = Z80::R24RegClass.contains(DstReg, SrcReg); + if (Is24Bit == Subtarget.is24Bit()) { + // Special case DE/HL = HL/DE as EX DE,HL. + if (KillSrc && canExchange(DstReg, SrcReg)) { + MachineInstrBuilder MIB = BuildMI(MBB, MI, DL, + get(Is24Bit ? Z80::EX24DE + : Z80::EX16DE)); + MIB->findRegisterUseOperand(SrcReg)->setIsKill(); + MIB->findRegisterDefOperand(SrcReg)->setIsDead(); + MIB->findRegisterUseOperand(DstReg)->setIsUndef(); + return; + } + bool IsSrcIndexReg = Z80::I16RegClass.contains(SrcReg) || + Z80::I24RegClass.contains(SrcReg); + // Special case copies from index registers when we have eZ80 ops. + if (Subtarget.hasEZ80Ops() && IsSrcIndexReg) { + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::LEA24ro : Z80::LEA16ro), DstReg) + .addReg(SrcReg, getKillRegState(KillSrc)).addImm(0); + return; + } + // If both are 24-bit then the upper byte needs to be preserved. + // Otherwise copies of index registers may need to use this method if: + // - We are optimizing for size and exactly one reg is an index reg because + // PUSH SrcReg \ POP DstReg is (2 + NumIndexRegs) bytes but slower + // LD DstRegLo,SrcRegLo \ LD DstRegHi,SrcRegHi is 4 bytes but faster + // - We don't have undocumented half index copies + bool IsDstIndexReg = Z80::I16RegClass.contains(DstReg) || + Z80::I24RegClass.contains(DstReg); + unsigned NumIndexRegs = IsSrcIndexReg + IsDstIndexReg; + bool OptSize = MBB.getParent()->getFunction().hasOptSize(); + if (Is24Bit || (NumIndexRegs == 1 && OptSize) || + (NumIndexRegs && !Subtarget.hasIndexHalfRegs())) { + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(SrcReg, getKillRegState(KillSrc)); + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::POP24r : Z80::POP16r), DstReg); + return; + } + } + // Otherwise, implement as two copies. A 16-bit copy should copy high and low + // 8 bits separately. + assert(Z80::R16RegClass.contains(DstReg, SrcReg) && "Unknown copy width"); + unsigned SubLo = Z80::sub_low; + unsigned SubHi = Z80::sub_high; + MCRegister DstLoReg = RI.getSubReg(DstReg, SubLo); + MCRegister SrcLoReg = RI.getSubReg(SrcReg, SubLo); + MCRegister DstHiReg = RI.getSubReg(DstReg, SubHi); + MCRegister SrcHiReg = RI.getSubReg(SrcReg, SubHi); + /*bool DstLoSrcHiOverlap = RI.regsOverlap(DstLoReg, SrcHiReg); + bool SrcLoDstHiOverlap = RI.regsOverlap(SrcLoReg, DstHiReg); + if (DstLoSrcHiOverlap && SrcLoDstHiOverlap) { + assert(KillSrc && + "Both parts of SrcReg and DstReg overlap but not killing source!"); + // e.g. EUHL = LUDE so just swap the operands + MCRegister OtherReg; + if (canExchange(DstLoReg, SrcLoReg)) { + BuildMI(MBB, MI, DL, get(Subtarget.is24Bit() ? Z80::EX24DE : Z80::EX16DE)) + .addReg(DstReg, RegState::ImplicitDefine) + .addReg(SrcReg, RegState::ImplicitKill); + } else if ((OtherReg = DstLoReg, RI.isSubRegisterEq(Z80::UHL, SrcLoReg)) || + (OtherReg = SrcLoReg, RI.isSubRegisterEq(Z80::UHL, DstLoReg))) { + BuildMI(MBB, MI, DL, + get(Subtarget.is24Bit() ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(OtherReg, RegState::Kill); + BuildMI(MBB, MI, DL, + get(Subtarget.is24Bit() ? Z80::EX24SP : Z80::EX16SP)); + BuildMI(MBB, MI, DL, get(Subtarget.is24Bit() ? Z80::POP24r : Z80::POP16r), + OtherReg); + } else { + BuildMI(MBB, MI, DL, + get(Subtarget.is24Bit() ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(SrcLoReg, RegState::Kill); + BuildMI(MBB, MI, DL, + get(Subtarget.is24Bit() ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(DstLoReg, RegState::Kill); + BuildMI(MBB, MI, DL, get(Subtarget.is24Bit() ? Z80::POP24r : Z80::POP16r), + SrcLoReg); + BuildMI(MBB, MI, DL, get(Subtarget.is24Bit() ? Z80::POP24r : Z80::POP16r), + DstLoReg); + } + // Check if top needs to be moved (e.g. EUHL = HUDE). + unsigned DstHiIdx = RI.getSubRegIndex(SrcLoReg, DstHiReg); + unsigned SrcHiIdx = RI.getSubRegIndex(DstLoReg, SrcHiReg); + if (DstHiIdx != SrcHiIdx) + copyPhysReg(MBB, MI, DL, DstHiReg, + RI.getSubReg(DstLoReg, SrcHiIdx), KillSrc); + } else if (DstLoSrcHiOverlap) { + // Copy out SrcHi before SrcLo overwrites it. + copyPhysReg(MBB, MI, DL, DstHiReg, SrcHiReg, KillSrc); + copyPhysReg(MBB, MI, DL, DstLoReg, SrcLoReg, KillSrc); + } else*/ { + // If SrcLoDstHiOverlap then copy out SrcLo before SrcHi overwrites it, + // otherwise the order doesn't matter. + copyPhysReg(MBB, MI, DL, DstLoReg, SrcLoReg, KillSrc); + copyPhysReg(MBB, MI, DL, DstHiReg, SrcHiReg, KillSrc); + } + --MI; + MI->addRegisterDefined(DstReg, &RI); + if (KillSrc) + MI->addRegisterKilled(SrcReg, &RI, true); +} + +static const MachineInstrBuilder &addSubReg(const MachineInstrBuilder &MIB, + Register Reg, unsigned Idx, + const MCRegisterInfo *TRI, + unsigned Flags = 0) { + if (Idx && Reg.isPhysical()) { + Reg = TRI->getSubReg(Reg, Idx); + Idx = 0; + } + return MIB.addReg(Reg, Flags, Idx); +} + +void Z80InstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + Register SrcReg, bool IsKill, int FI, + const TargetRegisterClass *TRC, + const TargetRegisterInfo *TRI) const { + unsigned Opc; + switch (TRI->getSpillSize(*TRC)) { + default: + llvm_unreachable("Unexpected regclass size"); + case 1: + Opc = Z80::LD8or; + break; + case 2: + Opc = Subtarget.has16BitEZ80Ops() ? Z80::LD16or : Z80::LD88or; + break; + case 3: + assert(Subtarget.is24Bit() && "Only 24-bit should have 3 byte stack slots"); + Opc = Z80::LD24or; + break; + } + BuildMI(MBB, MI, MBB.findDebugLoc(MI), get(Opc)).addFrameIndex(FI).addImm(0) + .addReg(SrcReg, getKillRegState(IsKill)); + return; + unsigned RC, LoOpc, LoIdx, HiOpc, HiIdx, HiOff; + bool Split = + Z80::splitReg(TRI->getSpillSize(*TRC), Z80::LD8or, Z80::LD16or, Z80::LD24or, + RC, LoOpc, LoIdx, HiOpc, HiIdx, HiOff, + Subtarget.has16BitEZ80Ops()); + addSubReg(BuildMI(MBB, MI, MBB.findDebugLoc(MI), get(LoOpc)) + .addFrameIndex(FI).addImm(0), SrcReg, LoIdx, TRI, + getKillRegState(IsKill)); + if (Split) { + MachineInstrBuilder HiMIB = addSubReg( + BuildMI(MBB, MI, MBB.findDebugLoc(MI), get(HiOpc)) + .addFrameIndex(FI).addImm(HiOff), SrcReg, HiIdx, TRI, + getKillRegState(IsKill)); + if (IsKill) + HiMIB->addRegisterKilled(SrcReg, TRI, true); + } +} + +void Z80InstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + Register DstReg, int FI, + const TargetRegisterClass *TRC, + const TargetRegisterInfo *TRI) const { + unsigned Opc; + switch (TRI->getSpillSize(*TRC)) { + default: + llvm_unreachable("Unexpected regclass size"); + case 1: + Opc = Z80::LD8ro; + break; + case 2: + Opc = Subtarget.has16BitEZ80Ops() ? Z80::LD16ro : Z80::LD88ro; + break; + case 3: + assert(Subtarget.is24Bit() && "Only 24-bit should have 3 byte stack slots"); + Opc = Z80::LD24ro; + break; + } + BuildMI(MBB, MI, MBB.findDebugLoc(MI), get(Opc), DstReg).addFrameIndex(FI) + .addImm(0); + return; + unsigned RC, LoOpc, LoIdx, HiOpc, HiIdx, HiOff; + bool Split = + Z80::splitReg(TRI->getSpillSize(*TRC), Z80::LD8ro, Z80::LD16ro, Z80::LD24ro, + RC, LoOpc, LoIdx, HiOpc, HiIdx, HiOff, + Subtarget.hasEZ80Ops()); + addSubReg(BuildMI(MBB, MI, MBB.findDebugLoc(MI), get(LoOpc)), DstReg, LoIdx, + TRI, RegState::DefineNoRead).addFrameIndex(FI).addImm(0); + if (Split) { + MachineInstrBuilder HiMIB = addSubReg( + BuildMI(MBB, MI, MBB.findDebugLoc(MI), get(HiOpc)), DstReg, HiIdx, + TRI, RegState::Define).addFrameIndex(FI).addImm(HiOff); + HiMIB->addRegisterDefined(DstReg, TRI); + } +} + +/// Return true and the FrameIndex if the specified +/// operand and follow operands form a reference to the stack frame. +bool Z80InstrInfo::isFrameOperand(const MachineInstr &MI, unsigned int Op, + int &FrameIndex) const { + if (MI.getOperand(Op).isFI() && + MI.getOperand(Op + 1).isImm() && MI.getOperand(Op + 1).getImm() == 0) { + FrameIndex = MI.getOperand(Op).getIndex(); + return true; + } + return false; +} + +static bool isFrameLoadOpcode(int Opcode) { + switch (Opcode) { + default: + return false; + case Z80::LD8ro: + case Z80::LD8go: + case Z80::LD16ro: + case Z80::LD88ro: + case Z80::LD24ro: + return true; + } +} +unsigned Z80InstrInfo::isLoadFromStackSlot(const MachineInstr &MI, + int &FrameIndex) const { + if (isFrameLoadOpcode(MI.getOpcode()) && !MI.getOperand(0).getSubReg() && + isFrameOperand(MI, 1, FrameIndex)) + return MI.getOperand(0).getReg(); + return 0; +} + +static bool isFrameStoreOpcode(int Opcode) { + switch (Opcode) { + default: + return false; + case Z80::LD8or: + case Z80::LD8og: + case Z80::LD16or: + case Z80::LD88or: + case Z80::LD24or: + return true; + } +} +unsigned Z80InstrInfo::isStoreToStackSlot(const MachineInstr &MI, + int &FrameIndex) const { + if (isFrameStoreOpcode(MI.getOpcode()) && !MI.getOperand(2).getSubReg() && + isFrameOperand(MI, 0, FrameIndex)) + return MI.getOperand(2).getReg(); + return 0; +} + +bool Z80InstrInfo::isReallyTriviallyReMaterializable(const MachineInstr &MI, + AAResults *AA) const { + switch (MI.getOpcode()) { + case Z80::LD8r0: + case Z80::LD24r0: + case Z80::LD24r_1: + return true; + } + return false; +} + +void Z80InstrInfo::reMaterialize(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, Register DstReg, + unsigned SubIdx, const MachineInstr &Orig, + const TargetRegisterInfo &TRI) const { + if (!Orig.modifiesRegister(Z80::F, &TRI) || + MBB.computeRegisterLiveness(&TRI, Z80::F, I) == + MachineBasicBlock::LQR_Dead) + return TargetInstrInfo::reMaterialize(MBB, I, DstReg, SubIdx, Orig, TRI); + // The instruction clobbers F. Re-materialize as LDri to avoid side effects. + unsigned Opc; + int Value; + switch (Orig.getOpcode()) { + default: llvm_unreachable("Unexpected instruction!"); + case Z80::LD8r0: Opc = Z80::LD8ri; Value = 0; break; + case Z80::LD24r0: Opc = Z80::LD24ri; Value = 0; break; + case Z80::LD24r_1: Opc = Z80::LD24ri; Value = -1; break; + } + const MachineOperand &OrigReg = Orig.getOperand(0); + MachineInstr &NewMI = + *BuildMI(MBB, I, Orig.getDebugLoc(), get(Opc)).add(OrigReg).addImm(Value); + NewMI.substituteRegister(OrigReg.getReg(), DstReg, SubIdx, TRI); +} + +void Z80InstrInfo:: +expandLoadStoreWord(const TargetRegisterClass *ARC, unsigned AOpc, + const TargetRegisterClass *ORC, unsigned OOpc, + MachineInstr &MI, unsigned RegIdx) const { + MCRegister Reg = MI.getOperand(RegIdx).getReg(); + assert(ARC->contains(Reg) != ORC->contains(Reg) && + "RegClasses should be covering and disjoint"); + MI.setDesc(get(ARC->contains(Reg) ? AOpc : OOpc)); + (void)ORC; +} + +bool Z80InstrInfo::expandPostRAPseudo(MachineInstr &MI) const { + DebugLoc DL = MI.getDebugLoc(); + MachineBasicBlock &MBB = *MI.getParent(); + MachineFunction &MF = *MBB.getParent(); + auto Next = ++MachineBasicBlock::iterator(MI); + MachineInstrBuilder MIB(MF, MI); + const TargetRegisterInfo &TRI = getRegisterInfo(); + bool Is24Bit = Subtarget.is24Bit(); + bool UseLEA = Is24Bit && !MF.getFunction().hasOptSize(); + LLVM_DEBUG(dbgs() << "\nZ80InstrInfo::expandPostRAPseudo:"; MI.dump()); + switch (unsigned Opc = MI.getOpcode()) { + default: + return false; + case Z80::RCF: + MI.setDesc(get(Z80::OR8ar)); + MIB.addReg(Z80::A, RegState::Undef); + break; + case Z80::LD8r0: + if (MI.getOperand(0).getReg() == Z80::A) { + MI.setDesc(get(Z80::XOR8ar)); + MI.getOperand(0).setIsUse(); + MI.getOperand(0).setIsUndef(); + MIB.addReg(Z80::A, RegState::ImplicitDefine); + } else { + MI.setDesc(get(Z80::LD8ri)); + MI.findRegisterDefOperand(Z80::F)->ChangeToImmediate(0); + } + break; + case Z80::LD24r0: + case Z80::LD24r_1: + if (MI.getOperand(0).getReg() == Z80::UHL) { + if (Opc == Z80::LD24r0) + expandPostRAPseudo(*BuildMI(MBB, MI, DL, get(Z80::RCF))); + else + BuildMI(MBB, MI, DL, get(Z80::SCF)); + MI.setDesc(get(Z80::SBC24aa)); + MI.getOperand(0).setImplicit(); + MIB.addReg(Z80::UHL, RegState::Implicit | RegState::Undef); + MIB.addReg(Z80::F, RegState::Implicit); + } else { + MI.setDesc(get(Z80::LD24ri)); + MI.findRegisterDefOperand(Z80::F) + ->ChangeToImmediate(Opc == Z80::LD24r0 ? 0 : -1); + } + break; + case Z80::CP16ao: + case Z80::CP24ao: { + MCRegister Reg = Opc == Z80::CP24ao ? Z80::UHL : Z80::HL; + if (MBB.computeRegisterLiveness(&TRI, Reg, Next) != + MachineBasicBlock::LQR_Dead) { + BuildMI(MBB, Next, DL, get(Opc == Z80::CP24ao ? Z80::ADD24ao + : Z80::ADD16ao), Reg) + .addReg(Reg).add(MI.getOperand(0)); + MI.getOperand(0).setIsKill(false); + } + LLVM_FALLTHROUGH; + } + case Z80::SUB16ao: + case Z80::SUB24ao: + expandPostRAPseudo(*BuildMI(MBB, MI, DL, get(Z80::RCF))); + MI.setDesc(get(Opc == Z80::CP24ao || Opc == Z80::SUB24ao ? Z80::SBC24ao + : Z80::SBC16ao)); + break; + case Z80::CP16a0: + case Z80::CP24a0: { + MCRegister Reg = Opc == Z80::CP24a0 ? Z80::UHL : Z80::HL; + MCRegister UndefReg = Opc == Z80::CP24a0 ? Z80::UBC : Z80::BC; + BuildMI(MBB, MI, DL, get(Opc == Z80::CP24a0 ? Z80::ADD24ao : Z80::ADD16ao), + Reg).addReg(Reg).addReg(UndefReg, RegState::Undef); + expandPostRAPseudo(*BuildMI(MBB, MI, DL, get(Z80::RCF))); + MI.setDesc(get(Opc == Z80::CP24a0 ? Z80::SBC24ao : Z80::SBC16ao)); + MIB.addReg(UndefReg, RegState::Undef); + break; + } + case Z80::LD8ro: + case Z80::LD8rp: { + MachineOperand &DstOp = MI.getOperand(0); + if (Z80::I8RegClass.contains(DstOp.getReg())) { + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)) + ->findRegisterUseOperand(Z80::AF)->setIsUndef(); + copyPhysReg(MBB, Next, DL, DstOp.getReg(), Z80::A, true); + DstOp.setReg(Z80::A); + BuildMI(MBB, Next, DL, get(Is24Bit ? Z80::POP24AF : Z80::POP16AF)); + } + MI.setDesc(get(Opc == Z80::LD8ro ? Z80::LD8go : Z80::LD8gp)); + break; + } + case Z80::LD88rp: + case Z80::LD88ro: { + assert(!Subtarget.has16BitEZ80Ops() && + "LD88rp/LD88ro should not be used on the ez80 in 16-bit mode"); + MachineOperand &DstOp = MI.getOperand(0); + MCRegister OrigReg = DstOp.getReg(); + MCRegister Reg = OrigReg; + const MachineOperand &AddrOp = MI.getOperand(1); + MCRegister AddrReg = AddrOp.getReg(); + unsigned LowOpc = Opc == Z80::LD88rp ? Z80::LD8rp : Z80::LD8ro; + unsigned HighOpc = + RI.isSubRegisterEq(Z80::UHL, AddrReg) ? Z80::LD8rp : Z80::LD8ro; + bool Index = Z80::I16RegClass.contains(Reg); + if (Index) + Reg = Z80::HL; + bool Overlap = RI.isSubRegisterEq(AddrReg, Reg); + if (Overlap) + Reg = Z80::DE; + MCRegister ScratchReg = + Is24Bit + ? TRI.getMatchingSuperReg(Reg, Z80::sub_short, &Z80::R24RegClass) + : Reg; + if (Index || Overlap) + // Save scratch register + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(ScratchReg, RegState::Undef); + MI.setDesc(get(HighOpc)); + DstOp.setReg(TRI.getSubReg(Reg, Z80::sub_high)); + if (LowOpc != HighOpc) { + assert(LowOpc == Z80::LD8rp && HighOpc == Z80::LD8ro && + "Can't offset low only"); + // Fixup if converting high to offset + MIB.addImm(1); + } + MIB = BuildMI(MBB, MI, DL, get(LowOpc), TRI.getSubReg(Reg, Z80::sub_low)) + .addReg(AddrReg); + if (HighOpc == Z80::LD8rp) { + // Compute high address + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::INC24r : Z80::INC16r), AddrReg) + .addReg(AddrReg); + if (!AddrOp.isKill()) + // Restore address register + BuildMI(MBB, Next, DL, get(Is24Bit ? Z80::DEC24r : Z80::DEC16r), + AddrReg).addReg(AddrReg); + } else if (LowOpc == Z80::LD8ro) { + // Fixup if both are offset + MachineOperand &OffOp = MI.getOperand(2); + MIB.add(OffOp); + OffOp.setImm(OffOp.getImm() + 1); + assert(isInt<8>(OffOp.getImm()) && "LD88 can't have maximum offset."); + } + if (Index) { + if (Reg == Z80::HL) + // Restore scratch register and prepare to set original register below + BuildMI(MBB, Next, DL, get(Is24Bit ? Z80::EX24SP : Z80::EX16SP), + ScratchReg).addReg(ScratchReg); + else + // Set original register directly + copyPhysReg(MBB, Next, DL, OrigReg, Reg); + } else if (Overlap) + copyPhysReg(MBB, Next, DL, OrigReg, Reg, true); + if (Index || Overlap) + // Restore scratch register or set original register + BuildMI(MBB, Next, DL, get(Is24Bit ? Z80::POP24r : Z80::POP16r), + Index && Reg == Z80::HL + ? Is24Bit ? TRI.getMatchingSuperReg(OrigReg, Z80::sub_short, + &Z80::R24RegClass) + : OrigReg + : ScratchReg); + expandPostRAPseudo(*MIB); + expandPostRAPseudo(MI); + break; + } + case Z80::LD8or: + case Z80::LD8pr: { + MachineOperand &SrcOp = MI.getOperand(MI.getNumExplicitOperands() - 1); + if (Z80::I8RegClass.contains(SrcOp.getReg())) { + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)) + ->findRegisterUseOperand(Z80::AF)->setIsUndef(); + copyPhysReg(MBB, MI, DL, Z80::A, SrcOp.getReg(), SrcOp.isKill()); + SrcOp.setReg(Z80::A); + SrcOp.setIsKill(); + BuildMI(MBB, Next, DL, get(Is24Bit ? Z80::POP24AF : Z80::POP16AF)); + } + MI.setDesc(get(Opc == Z80::LD8or ? Z80::LD8og : Z80::LD8pg)); + break; + } + case Z80::LD88pr: + case Z80::LD88or: { + assert(!Subtarget.has16BitEZ80Ops() && + "LD88pr/LD88or should not be used on the ez80 in 16-bit mode"); + const MachineOperand &AddrOp = MI.getOperand(0); + MCRegister AddrReg = AddrOp.getReg(); + MachineOperand &SrcOp = MI.getOperand(MI.getNumExplicitOperands() - 1); + MCRegister OrigReg = SrcOp.getReg(); + MCRegister Reg = OrigReg; + unsigned LowOpc = Opc == Z80::LD88pr ? Z80::LD8pr : Z80::LD8or; + unsigned HighOpc = + RI.isSubRegisterEq(Z80::UHL, AddrReg) ? Z80::LD8pr : Z80::LD8or; + bool Index = Z80::I16RegClass.contains(Reg); + if (Index) + Reg = Z80::HL; + bool Overlap = RI.isSubRegisterEq(AddrReg, Reg); + if (Overlap && Index) { + // Just use DE instead, so there's no overlap to worry about. + Reg = Z80::DE; + Overlap = false; + assert(!RI.isSubRegisterEq(AddrReg, Reg) && "Shouldn't overlap"); + } + assert((!Overlap || HighOpc == Z80::LD8pr) && + "If we are overlapping, then high shouldn't be offset"); + MCRegister ScratchReg = + Is24Bit + ? TRI.getMatchingSuperReg(Reg, Z80::sub_short, &Z80::R24RegClass) + : Reg; + MCRegister OrigHighReg = TRI.getSubReg(Reg, Z80::sub_high); + MCRegister HighReg = OrigHighReg; + if (Index) { + MCRegister OrigSuperReg = + TRI.getMatchingSuperReg(OrigReg, Z80::sub_short, &Z80::R24RegClass); + // Save scratch register or prepare to set scratch register + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(UseLEA || Reg == Z80::DE ? ScratchReg + : Is24Bit ? OrigSuperReg : OrigReg); + if (UseLEA) + // Set scratch register + BuildMI(MBB, MI, DL, get(Z80::LEA24ro), ScratchReg) + .addReg(OrigSuperReg).addImm(0); + else if (Reg == Z80::HL) + // Save and set scratch register + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::EX24SP : Z80::EX16SP), + ScratchReg).addReg(ScratchReg); + else + // Set scratch register directly + copyPhysReg(MBB, MI, DL, Reg, OrigReg); + } else if (Overlap) { + // Save A + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)) + ->findRegisterUseOperand(Z80::AF)->setIsUndef(); + HighReg = Z80::A; + } + MI.setDesc(get(HighOpc)); + SrcOp.setReg(TRI.getSubReg(Reg, Z80::sub_high)); + if (LowOpc != HighOpc) { + assert(LowOpc == Z80::LD8pr && HighOpc == Z80::LD8or && + "Can't offset low only"); + // Fixup if converting high to offset + SrcOp.ChangeToImmediate(1); + MIB.addReg(HighReg); + } else + SrcOp.setReg(HighReg); + MIB = BuildMI(MBB, MI, DL, get(LowOpc)).addReg(AddrReg); + if (HighOpc == Z80::LD8pr) { + if (Overlap) { + assert(!Index && "Should have changed to DE above"); + // Save high reg in A so it isn't clobbered by the inc below + copyPhysReg(MBB, MI, DL, HighReg, OrigHighReg, false); + } + // Compute high address + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::INC24r : Z80::INC16r), AddrReg) + .addReg(AddrReg); + if (!AddrOp.isKill()) + // Restore address register + BuildMI(MBB, Next, DL, get(Is24Bit ? Z80::DEC24r : Z80::DEC16r), + AddrReg).addReg(AddrReg); + } else if (LowOpc == Z80::LD8or) { + // Fixup if both are offset + MachineOperand &OffOp = MI.getOperand(1); + MIB.add(OffOp); + OffOp.setImm(OffOp.getImm() + 1); + assert(isInt<8>(OffOp.getImm()) && "LD88 can't have maximum offset."); + } + MIB.addReg(TRI.getSubReg(Reg, Z80::sub_low)); + if (Index) + // Restore scratch register + BuildMI(MBB, Next, DL, get(Is24Bit ? Z80::POP24r : Z80::POP16r), + ScratchReg); + else if (Overlap) + // Restore A + BuildMI(MBB, Next, DL, get(Is24Bit ? Z80::POP24AF : Z80::POP16AF)); + expandPostRAPseudo(*MIB); + expandPostRAPseudo(MI); + break; + } + case Z80::LD16rm: + expandLoadStoreWord(&Z80::A16RegClass, Z80::LD16am, + &Z80::O16RegClass, Z80::LD16om, MI, 0); + break; + case Z80::LD24rm: + expandLoadStoreWord(&Z80::A24RegClass, Z80::LD24am, + &Z80::O24RegClass, Z80::LD24om, MI, 0); + break; + case Z80::LD16mr: + expandLoadStoreWord(&Z80::A16RegClass, Z80::LD16ma, + &Z80::O16RegClass, Z80::LD16mo, MI, 1); + break; + case Z80::LD24mr: + expandLoadStoreWord(&Z80::A24RegClass, Z80::LD24ma, + &Z80::O24RegClass, Z80::LD24mo, MI, 1); + break; + case Z80::CALL16r: + case Z80::CALL24r: { + const char *Symbol; + switch (MIB->getOperand(0).getReg()) { + default: llvm_unreachable("Unexpected indcall register"); + case Z80::HL: case Z80::UHL: Symbol = "_indcallhl"; break; + case Z80::IX: case Z80::UIX: Symbol = "_indcallix"; break; + case Z80::IY: case Z80::UIY: Symbol = "_indcall"; break; + } + MI.setDesc(get(Opc == Z80::CALL24r ? Z80::CALL24 : Z80::CALL16)); + MI.getOperand(0).ChangeToES(Symbol); + break; + } + case Z80::EI_RETI: + BuildMI(MBB, MI, DL, get(Z80::EI)); + MI.setDesc(get(Is24Bit ? Z80::RETI24 : Z80::RETI16)); + break; + case Z80::TCRETURN16: + case Z80::TCRETURN24: + MI.setDesc(get(Z80::JQ)); + break; + case Z80::TCRETURN16r: + MI.setDesc(get(Z80::JP16r)); + break; + case Z80::TCRETURN24r: + MI.setDesc(get(Z80::JP24r)); + break; + } + LLVM_DEBUG(MIB->dump()); + return true; +} + +bool Z80InstrInfo::analyzeCompare(const MachineInstr &MI, Register &SrcReg, + Register &SrcReg2, int &CmpMask, + int &CmpValue) const { + switch (MI.getOpcode()) { + default: return false; + case Z80::OR8ar: + SrcReg = Z80::A; + if (MI.getOperand(1).getReg() != SrcReg) + return false; + // Compare against zero. + SrcReg2 = 0; + CmpMask = ~0; + CmpValue = 0; + break; + case Z80::CP8ai: + case Z80::SUB8ai: + SrcReg = Z80::A; + SrcReg2 = 0; + CmpMask = CmpValue = 0; + if (MI.getOperand(0).isImm()) { + CmpMask = ~0; + CmpValue = MI.getOperand(0).getImm(); + } + break; + case Z80::CP8ar: + case Z80::SUB8ar: + SrcReg = Z80::A; + SrcReg2 = MI.getOperand(0).getReg(); + CmpMask = CmpValue = 0; + break; + case Z80::CP8ap: + case Z80::CP8ao: + case Z80::SUB8ap: + case Z80::SUB8ao: + SrcReg = Z80::A; + SrcReg2 = CmpMask = CmpValue = 0; + break; + } + MachineBasicBlock::const_reverse_iterator I = MI, E = MI.getParent()->rend(); + while (++I != E && I->isFullCopy()) + for (Register *Reg : {&SrcReg, &SrcReg2}) + if (Reg->isPhysical() && *Reg == I->getOperand(0).getReg()) + *Reg = I->getOperand(1).getReg(); + return true; +} + +/// Check whether the first instruction, whose only purpose is to update flags, +/// can be made redundant. CP8ar is made redundant by SUB8ar if the operands are +/// the same. +/// SrcReg, SrcReg2: register operands for FlagI. +/// ImmValue: immediate for FlagI if it takes an immediate. +inline static bool isRedundantFlagInstr(MachineInstr &FI, Register SrcReg, + Register SrcReg2, int ImmMask, + int ImmValue, MachineInstr &OI) { + if (ImmMask) + return (FI.getOpcode() == Z80::CP8ai && OI.getOpcode() == Z80::SUB8ai) && + OI.getOperand(1).getImm() == ImmValue; + return (FI.getOpcode() == Z80::CP8ar && OI.getOpcode() == Z80::SUB8ar) && + OI.getOperand(1).getReg() == SrcReg2; +} + +/// Check whether the instruction sets the sign and zero flag based on its +/// result. +inline static bool isSZSettingInstr(MachineInstr &MI) { + switch (MI.getOpcode()) { + default: return false; + case Z80::INC8r: case Z80::INC8p: case Z80::INC8o: + case Z80::DEC8r: case Z80::DEC8p: case Z80::DEC8o: + case Z80::ADD8ar: case Z80::ADD8ai: case Z80::ADD8ap: case Z80::ADD8ao: + case Z80::ADC8ar: case Z80::ADC8ai: case Z80::ADC8ap: case Z80::ADC8ao: + case Z80::SUB8ar: case Z80::SUB8ai: case Z80::SUB8ap: case Z80::SUB8ao: + case Z80::SBC8ar: case Z80::SBC8ai: case Z80::SBC8ap: case Z80::SBC8ao: + case Z80::AND8ar: case Z80::AND8ai: case Z80::AND8ap: case Z80::AND8ao: + case Z80::XOR8ar: case Z80::XOR8ai: case Z80::XOR8ap: case Z80::XOR8ao: + case Z80:: OR8ar: case Z80:: OR8ai: case Z80:: OR8ap: case Z80:: OR8ao: + case Z80::SBC16ao:case Z80::NEG: case Z80::ADC16ao: + case Z80::SUB16ao:case Z80::SUB24ao: + case Z80::RLC8r: case Z80::RLC8p: case Z80::RLC8o: + case Z80::RRC8r: case Z80::RRC8p: case Z80::RRC8o: + case Z80:: RL8r: case Z80:: RL8p: case Z80:: RL8o: + case Z80:: RR8r: case Z80:: RR8p: case Z80:: RR8o: + case Z80::SLA8r: case Z80::SLA8p: case Z80::SLA8o: + case Z80::SRA8r: case Z80::SRA8p: case Z80::SRA8o: + case Z80::SRL8r: case Z80::SRL8p: case Z80::SRL8o: + return true; + } +} + +/// Check if there exists an earlier instruction that operates on the same +/// source operands and sets flags in the same way as Compare; remove Compare if +/// possible. +bool Z80InstrInfo::optimizeCompareInstr(MachineInstr &CmpInstr, Register SrcReg, + Register SrcReg2, int CmpMask, + int CmpValue, + const MachineRegisterInfo *MRI) const { + // If we are comparing against zero, check whether we can use MI to update F. + bool IsCmpZero = CmpMask && !CmpValue; + + // Check whether we can replace SUB with CP. + unsigned CpOpc; + switch (CmpInstr.getOpcode()) { + default: CpOpc = 0; break; + case Z80::SUB8ai: CpOpc = IsCmpZero ? Z80::OR8ar : Z80::CP8ai; break; + case Z80::SUB8ar: CpOpc = Z80::CP8ar; break; + case Z80::SUB8ap: CpOpc = Z80::CP8ap; break; + case Z80::SUB8ao: CpOpc = Z80::CP8ao; break; + } + if (CpOpc) { + int DeadDef = CmpInstr.findRegisterDefOperandIdx(Z80::A, /*isDead*/true); + if (DeadDef == -1) + return false; + // There is no use of the destination register, so we replace SUB with CP. + CmpInstr.setDesc(get(CpOpc)); + if (CpOpc == Z80::OR8ar) + CmpInstr.getOperand(0).ChangeToRegister(Z80::A, false); + else + CmpInstr.RemoveOperand(DeadDef); + } + + // Get the unique definition of SrcReg. + MachineInstr *MI = MRI->getUniqueVRegDef(SrcReg); + if (!MI) return false; + + MachineBasicBlock::iterator I = CmpInstr, Def = MI; + + const TargetRegisterInfo *TRI = &getRegisterInfo(); + for (auto RI = ++Def.getReverse(), RE = MI->getParent()->rend(); + MI->isFullCopy() && RI != RE; ++RI) + if (RI->definesRegister(MI->getOperand(1).getReg(), TRI)) + MI = &*RI; + + // If MI is not in the same BB as CmpInstr, do not optimize. + if (IsCmpZero && (MI->getParent() != CmpInstr.getParent() || + !isSZSettingInstr(*MI))) + return false; + + // We are searching for an earlier instruction, which will be stored in + // SubInstr, that can make CmpInstr redundant. + MachineInstr *SubInstr = nullptr; + + // We iterate backwards, starting from the instruction before CmpInstr, and + // stopping when we reach the definition of a source register or the end of + // the BB. RI points to the instruction before CmpInstr. If the definition is + // in this BB, RE points to it, otherwise RE is the beginning of the BB. + MachineBasicBlock::reverse_iterator RE = CmpInstr.getParent()->rend(); + if (CmpInstr.getParent() == MI->getParent()) + RE = Def.getReverse(); // points to MI + for (auto RI = ++I.getReverse(); RI != RE; ++RI) { + MachineInstr &Instr = *RI; + // Check whether CmpInstr can be made redundant by the current instruction. + if (!IsCmpZero && isRedundantFlagInstr(CmpInstr, SrcReg, SrcReg2, CmpMask, + CmpValue, Instr)) { + SubInstr = &Instr; + break; + } + + // If this instruction modifies F, we can't remove CmpInstr. + if (Instr.modifiesRegister(Z80::F, TRI)) + return false; + } + + // Return false if no candidates exist. + if (!IsCmpZero && !SubInstr) + return false; + + // Scan forward from the instruction after CmpInstr for uses of F. + // It is safe to remove CmpInstr if F is redefined or killed. + // If we are at the end of the BB, we need to check whether F is live-out. + bool IsSafe = false; + MachineBasicBlock::iterator E = CmpInstr.getParent()->end(); + for (++I; I != E; ++I) { + const MachineInstr &Instr = *I; + bool ModifiesFlags = Instr.modifiesRegister(Z80::F, TRI); + bool UsesFlags = Instr.readsRegister(Z80::F, TRI); + if (ModifiesFlags && !UsesFlags) { + IsSafe = true; + break; + } + if (!ModifiesFlags && !UsesFlags) + continue; + if (IsCmpZero) { + Z80::CondCode OldCC; + switch (Instr.getOpcode()) { + default: + OldCC = Z80::COND_INVALID; + break; + case Z80::JQCC: + OldCC = static_cast(Instr.getOperand(1).getImm()); + break; + case Z80::ADC8ar: case Z80::ADC8ai: case Z80::ADC8ap: case Z80::ADC8ao: + case Z80::SBC8ar: case Z80::SBC8ai: case Z80::SBC8ap: case Z80::SBC8ao: + case Z80::SBC16ao:case Z80::ADC16ao: + OldCC = Z80::COND_C; + break; + } + switch (OldCC) { + default: break; + case Z80::COND_NC: case Z80::COND_C: + case Z80::COND_PO: case Z80::COND_PE: + // CF or PV are used, we can't perform this optimization. + return false; + } + } + if (ModifiesFlags || Instr.killsRegister(Z80::F, TRI)) { + // It is safe to remove CmpInstr if F is updated again or killed. + IsSafe = true; + break; + } + } + if (IsCmpZero && !IsSafe) { + MachineBasicBlock *MBB = CmpInstr.getParent(); + for (MachineBasicBlock *Successor : MBB->successors()) + if (Successor->isLiveIn(Z80::F)) + return false; + } + + // The instruction to be updated is either Sub or MI. + if (IsCmpZero) + SubInstr = MI; + + // Make sure Sub instruction defines F and mark the def live. + unsigned i = 0, e = SubInstr->getNumOperands(); + for (; i != e; ++i) { + MachineOperand &MO = SubInstr->getOperand(i); + if (MO.isReg() && MO.isDef() && MO.getReg() == Z80::F) { + MO.setIsDead(false); + break; + } + } + assert(i != e && "Unable to locate a def EFLAGS operand"); + + CmpInstr.eraseFromParent(); + return true; + + // Check whether we can replace SUB with CMP. + switch (CmpInstr.getOpcode()) { + default: return false; + case Z80::SUB8ai: + // cp a,0 -> or a,a (a szhc have same behavior) + // FIXME: This doesn't work if the pv flag is used. + if (!CmpInstr.getOperand(0).getImm()) { + CmpInstr.setDesc(get(Z80::OR8ar)); + CmpInstr.getOperand(0).ChangeToRegister(Z80::A, /*isDef=*/false); + return true; + } + LLVM_FALLTHROUGH; + case Z80::SUB8ar: + case Z80::SUB8ap: + case Z80::SUB8ao: { + if (!CmpInstr.registerDefIsDead(Z80::A)) + return false; + // There is no use of the destination register, we can replace SUB with CMP. + unsigned NewOpcode = 0; + switch (CmpInstr.getOpcode()) { + default: llvm_unreachable("Unreachable!"); + case Z80::SUB8ai: NewOpcode = Z80::CP8ai; break; + case Z80::SUB8ar: NewOpcode = Z80::CP8ar; break; + case Z80::SUB8ap: NewOpcode = Z80::CP8ap; break; + case Z80::SUB8ao: NewOpcode = Z80::CP8ao; break; + } + CmpInstr.setDesc(get(NewOpcode)); + //CmpInstr.findRegisterDefOperand(Z80::A)->setIsDead(false); + //BuildMI(*CmpInstr.getParent(), ++MachineBasicBlock::iterator(CmpInstr), + // CmpInstr.getDebugLoc(), get(TargetOpcode::COPY), SrcReg) + // .addReg(Z80::A, RegState::Kill); + return true; + } + } +} + +MachineInstr * +Z80InstrInfo::foldMemoryOperandImpl(MachineFunction &MF, MachineInstr &MI, + ArrayRef Ops, + MachineBasicBlock::iterator InsertPt, + int FrameIndex, LiveIntervals *LIS, + VirtRegMap *VRM) const { + return nullptr; + bool Is24Bit = Subtarget.is24Bit(); + MachineBasicBlock &MBB = *InsertPt->getParent(); + if (Ops.size() == 1 && Ops[0] == 1 && MI.isFullCopy()) { + unsigned DstReg = MI.getOperand(0).getReg(); + if (Register::isPhysicalRegister(DstReg)) { + unsigned Opc; + if (Z80::R8RegClass.contains(DstReg)) { + Opc = Z80::LD8ro; + } else { + assert((Is24Bit ? Z80::R24RegClass : Z80::R16RegClass) + .contains(DstReg) && "Unexpected physical reg"); + Opc = Is24Bit ? Z80::LD24ro : Z80::LD16ro; + } + return BuildMI(MBB, InsertPt, MI.getDebugLoc(), get(Opc), DstReg) + .addFrameIndex(FrameIndex).addImm(0); + } + } + dbgs() << Ops.size() << ": "; + for (unsigned Op : Ops) + dbgs() << Op << ' '; + MI.dump(); + return nullptr; +} +MachineInstr * +Z80InstrInfo::foldMemoryOperandImpl(MachineFunction &MF, MachineInstr &MI, + ArrayRef Ops, + MachineBasicBlock::iterator InsertPt, + MachineInstr &LoadMI, + LiveIntervals *LIS) const { + return nullptr; + llvm_unreachable("Unimplemented"); +} diff --git a/llvm/lib/Target/Z80/Z80InstrInfo.h b/llvm/lib/Target/Z80/Z80InstrInfo.h new file mode 100644 index 00000000000000..b163805953e553 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80InstrInfo.h @@ -0,0 +1,201 @@ +//===-- Z80InstrInfo.h - Z80 Instruction Information ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Z80 implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80INSTRINFO_H +#define LLVM_LIB_TARGET_Z80_Z80INSTRINFO_H + +#include "Z80RegisterInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" + +#define GET_INSTRINFO_HEADER +#include "Z80GenInstrInfo.inc" + +namespace llvm { +class Z80Subtarget; + +namespace Z80 { + // Z80 specific condition code. These correspond to Z80_*_COND in + // Z80InstrInfo.td. They must be kept in synch. +enum CondCode { + COND_NZ = 0, + COND_Z = 1, + COND_NC = 2, + COND_C = 3, + LAST_SIMPLE_COND = COND_C, + + COND_PO = 4, + COND_PE = 5, + COND_P = 6, + COND_M = 7, + LAST_VALID_COND = COND_M, + + COND_INVALID +}; + +CondCode GetBranchConditionForPredicate(CmpInst::Predicate Pred, bool &IsSigned, + bool &IsSwapped, bool &IsConst); + +/// GetOppositeBranchCondition - Return the inverse of the specified cond, +/// e.g. turning COND_Z to COND_NZ. +CondCode GetOppositeBranchCondition(CondCode CC); + +bool splitReg(unsigned ByteSize, unsigned Opc8, unsigned Opc16, unsigned Opc24, + unsigned &RC, unsigned &LoOpc, unsigned &LoIdx, unsigned &HiOpc, + unsigned &HiIdx, unsigned &HiOff, bool Has16BitEZ80Ops); +} // end namespace Z80; + +namespace Z80II { + enum { + PrefixShift = 0, + NoPrefix = 0 << PrefixShift, + CBPrefix = 1 << PrefixShift, + DDPrefix = 2 << PrefixShift, + DDCBPrefix = 3 << PrefixShift, + EDPrefix = 4 << PrefixShift, + FDPrefix = 5 << PrefixShift, + FDCBPrefix = 6 << PrefixShift, + AnyIndexPrefix = 7 << PrefixShift, + PrefixMask = 7 << PrefixShift, + IndexedIndexPrefix = 8 << PrefixShift, + + ModeShift = 4, + AnyMode = 0 << ModeShift, + CurMode = 1 << ModeShift, + Z80Mode = 2 << ModeShift, + EZ80Mode = 3 << ModeShift, + ModeMask = 3 << ModeShift, + + HasImm = 1 << 6, + HasOff = 1 << 7, + + OpcodeShift = 8, + OpcodeMask = 0xFF << OpcodeShift + }; +} // end namespace Z80II; + +class Z80InstrInfo final : public Z80GenInstrInfo { + Z80Subtarget &Subtarget; + const Z80RegisterInfo RI; + + virtual void anchor(); + +public: + explicit Z80InstrInfo(Z80Subtarget &STI); + + /// getRegisterInfo - TargetInstrInfo is a superset of MRegister info. As + /// such, whenever a client has an instance of instruction info, it should + /// always be able to get register info as well (through this method). + /// + const Z80RegisterInfo &getRegisterInfo() const { return RI; } + + int getSPAdjust(const MachineInstr &MI) const override; + + int64_t getFrameAdjustment(const MachineInstr &MI) const { + assert(isFrameInstr(MI) && "Not a frame instruction"); + int64_t Amount = getFrameSize(MI) - + MI.getOperand(MI.getNumExplicitOperands() - 1).getImm(); + return isFrameSetup(MI) ? -Amount : Amount; + } + + unsigned getInstSizeInBytes(const MachineInstr &MI) const override; + + // Branch analysis. + bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const override; + + unsigned removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved = nullptr) const override; + unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + MachineBasicBlock *FBB, ArrayRef Cond, + const DebugLoc &DL, + int *BytesAdded = nullptr) const override; + bool + reverseBranchCondition(SmallVectorImpl &Cond) const override; + + void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + const DebugLoc &DL, MCRegister DstReg, MCRegister SrcReg, + bool KillSrc = false) const override; + void storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, Register SrcReg, + bool isKill, int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const override; + unsigned isStoreToStackSlot(const MachineInstr &MI, + int &FrameIndex) const override; + void loadRegFromStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, Register DstReg, + int FrameIndex, const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const override; + unsigned isLoadFromStackSlot(const MachineInstr &MI, + int &FrameIndex) const override; + + bool isReallyTriviallyReMaterializable(const MachineInstr &MI, + AAResults *AA) const override; + void reMaterialize(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, + Register DstReg, unsigned SubIdx, const MachineInstr &Orig, + const TargetRegisterInfo &TRI) const override; + + bool expandPostRAPseudo(MachineInstr &MI) const override; + + /// For a comparison instruction, return the source registers in SrcReg and + /// SrcReg2 if having two register operands, and the value it compares against + /// in CmpValue. Return true if the comparison instruction can be analyzed. + bool analyzeCompare(const MachineInstr &MI, Register &SrcReg, + Register &SrcReg2, int &CmpMask, + int &CmpValue) const override; + /// Check if there exists an earlier instruction that operates on the same + /// source operands and sets flags in the same way as Compare; remove Compare + /// if possible. + bool optimizeCompareInstr(MachineInstr &CmpInstr, Register SrcReg, + Register SrcReg2, int CmpMask, int CmpValue, + const MachineRegisterInfo *MRI) const override; + + /// Check whether the target can fold a load that feeds a subreg operand + /// (or a subreg operand that feeds a store). + bool isSubregFoldable() const override { return true; } + + MachineInstr * + foldMemoryOperandImpl(MachineFunction &MF, MachineInstr &MI, + ArrayRef Ops, + MachineBasicBlock::iterator InsertPt, int FrameIndex, + LiveIntervals *LIS = nullptr, + VirtRegMap *VRM = nullptr) const override; + MachineInstr * + foldMemoryOperandImpl(MachineFunction &MF, MachineInstr &MI, + ArrayRef Ops, + MachineBasicBlock::iterator InsertPt, + MachineInstr &LoadMI, + LiveIntervals *LIS = nullptr) const override; + +private: + /// canExchange - This returns whether the two instructions can be directly + /// exchanged with one EX instruction. Since the only register exchange + /// instruction is EX DE,HL, simply returns whether the two arguments are + /// super-registers of E and L, in any order. + bool canExchange(MCRegister RegA, MCRegister RegB) const; + + /// isFrameOperand - Return true and the FrameIndex if the specified + /// operand and follow operands form a reference to the stack frame. + bool isFrameOperand(const MachineInstr &MI, unsigned int Op, + int &FrameIndex) const; + + void expandLoadStoreWord(const TargetRegisterClass *ARC, unsigned AOpc, + const TargetRegisterClass *ORC, unsigned OOpc, + MachineInstr &MI, unsigned RegIdx) const; +}; + +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/Z80/Z80InstrInfo.td b/llvm/lib/Target/Z80/Z80InstrInfo.td new file mode 100644 index 00000000000000..7845fbc32d59f1 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80InstrInfo.td @@ -0,0 +1,1076 @@ +//===-- Z80InstrInfo.td - Main Z80 Instruction Definition --*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the Z80 instruction set, defining the instructions, and +// properties of the instructions which are needed for code generation, machine +// code emission, and analysis. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Type Constraints. +//===----------------------------------------------------------------------===// +class SDTCisChain : SDTCisVT; +class SDTCisI8 : SDTCisVT; +class SDTCisFlag : SDTCisI8; +class SDTCisI16 : SDTCisVT; +class SDTCisI24 : SDTCisVT; +class SDTCisPtr : SDTCisVT; + +def p0 : LLT; + +//===----------------------------------------------------------------------===// +// Type Profiles. +//===----------------------------------------------------------------------===// +def SDTUnOpRF : SDTypeProfile<2, 1, [SDTCisInt<0>, + SDTCisFlag<1>, + SDTCisSameAs<2, 0>]>; +def SDTUnOpRFF : SDTypeProfile<2, 2, [SDTCisInt<0>, + SDTCisFlag<1>, + SDTCisSameAs<2, 0>, + SDTCisFlag<3>]>; +def SDTBinOpRF : SDTypeProfile<2, 2, [SDTCisInt<0>, + SDTCisFlag<1>, + SDTCisSameAs<2, 0>, + SDTCisSameAs<3, 0>]>; +def SDTBinOpRFF : SDTypeProfile<2, 3, [SDTCisInt<0>, + SDTCisFlag<1>, + SDTCisSameAs<2, 0>, + SDTCisSameAs<3, 0>, + SDTCisFlag<4>]>; +def SDTBinOpF : SDTypeProfile<1, 2, [SDTCisFlag<0>, + SDTCisInt<1>, + SDTCisSameAs<2, 1>]>; +def SDTBitOpF : SDTypeProfile<1, 2, [SDTCisFlag<0>, + SDTCisI8<1>, + SDTCisI8<2>]>; +def SDTBitOpR : SDTypeProfile<1, 2, [SDTCisI8<0>, + SDTCisI8<1>, + SDTCisI8<2>]>; + +def SDTZ80Wrapper : SDTypeProfile<1, 1, [SDTCisPtrTy<0>, + SDTCisSameAs<1, 0>]>; +def SDT_Z80mlt : SDTypeProfile<1, 1, [SDTCisI16<0>, SDTCisI16<1>]>; +def SDT_Z80sext : SDTypeProfile<1, 1, [SDTCisInt<0>, SDTCisFlag<1>]>; +def SDT_Z80TCRet : SDTypeProfile<0, 1, [SDTCisPtrTy<0>]>; +def SDT_Z80Call : SDTypeProfile<0, -1, [SDTCisPtr<0>]>; +def SDT_Z80BrCond : SDTypeProfile<0, 3, [SDTCisChain<0>, + SDTCisI8<1>, + SDTCisFlag<2>]>; +def SDT_Z80Select : SDTypeProfile<1, 4, [SDTCisInt<0>, + SDTCisSameAs<1, 0>, + SDTCisSameAs<2, 0>, + SDTCisI8<3>, + SDTCisI8<4>]>; +def SDT_Z80Pop : SDTypeProfile<1, 0, [SDTCisPtr<0>]>; +def SDT_Z80Push : SDTypeProfile<0, 1, [SDTCisPtr<0>]>; + +//===----------------------------------------------------------------------===// +// Z80 specific DAG Nodes. +// + +def Z80Wrapper : SDNode<"Z80ISD::Wrapper", SDTZ80Wrapper>; +def Z80rlc_flag : SDNode<"Z80ISD::RLC", SDTUnOpRF>; +def Z80rrc_flag : SDNode<"Z80ISD::RRC", SDTUnOpRF>; +def Z80rl_flag : SDNode<"Z80ISD::RL", SDTUnOpRFF>; +def Z80rr_flag : SDNode<"Z80ISD::RR", SDTUnOpRFF>; +def Z80sla_flag : SDNode<"Z80ISD::SLA", SDTUnOpRF>; +def Z80sra_flag : SDNode<"Z80ISD::SRA", SDTUnOpRF>; +def Z80srl_flag : SDNode<"Z80ISD::SRL", SDTUnOpRF>; +def Z80bit_flag : SDNode<"Z80ISD::BIT", SDTBitOpF>; +def Z80res_flag : SDNode<"Z80ISD::RES", SDTBitOpR>; +def Z80set_flag : SDNode<"Z80ISD::SET", SDTBitOpR>; +def Z80inc_flag : SDNode<"Z80ISD::INC", SDTUnOpRF>; +def Z80dec_flag : SDNode<"Z80ISD::DEC", SDTUnOpRF>; +def Z80add_flag : SDNode<"Z80ISD::ADD", SDTBinOpRF, [SDNPCommutative]>; +def Z80adc_flag : SDNode<"Z80ISD::ADC", SDTBinOpRFF>; +def Z80sub_flag : SDNode<"Z80ISD::SUB", SDTBinOpRF>; +def Z80sbc_flag : SDNode<"Z80ISD::SBC", SDTBinOpRFF>; +def Z80and_flag : SDNode<"Z80ISD::AND", SDTBinOpRF, [SDNPCommutative]>; +def Z80xor_flag : SDNode<"Z80ISD::XOR", SDTBinOpRF, [SDNPCommutative]>; +def Z80or_flag : SDNode<"Z80ISD::OR", SDTBinOpRF, [SDNPCommutative]>; +def Z80cp_flag : SDNode<"Z80ISD::CP", SDTBinOpF>; +def Z80tst_flag : SDNode<"Z80ISD::TST", SDTBinOpF, [SDNPCommutative]>; +def Z80mlt : SDNode<"Z80ISD::MLT", SDT_Z80mlt>; +def Z80sext : SDNode<"Z80ISD::SEXT", SDT_Z80sext>; +def Z80retflag : SDNode<"Z80ISD::RET_FLAG", SDTNone, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; +def Z80retnflag : SDNode<"Z80ISD::RETN_FLAG", SDTNone, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; +def Z80retiflag : SDNode<"Z80ISD::RETI_FLAG", SDTNone, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; +def Z80tcret : SDNode<"Z80ISD::TC_RETURN", SDT_Z80TCRet, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; +def Z80call : SDNode<"Z80ISD::CALL", SDT_Z80Call, + [SDNPHasChain, SDNPOutGlue, SDNPOptInGlue, + SDNPVariadic]>; +def Z80brcond : SDNode<"Z80ISD::BRCOND", SDT_Z80BrCond, [SDNPHasChain]>; +def Z80select : SDNode<"Z80ISD::SELECT", SDT_Z80Select>; +def Z80pop : SDNode<"Z80ISD::POP", SDT_Z80Pop, + [SDNPHasChain, SDNPMayLoad]>; +def Z80push : SDNode<"Z80ISD::PUSH", SDT_Z80Push, + [SDNPHasChain, SDNPMayStore]>; + +//===----------------------------------------------------------------------===// +// Z80 Instruction Predicate Definitions. +// +def In16BitMode : Predicate<"Subtarget->is16Bit()">, + AssemblerPredicate<(all_of Mode16Bit), "16-bit mode">; +def In24BitMode : Predicate<"Subtarget->is24Bit()">, + AssemblerPredicate<(all_of Mode24Bit), "24-bit mode">; +def HaveUndocOps : Predicate<"Subtarget->hasUndocOps()">, + AssemblerPredicate<(all_of FeatureUndoc), + "undocumented ops">; +def HaveZ180Ops : Predicate<"Subtarget->hasZ180Ops()">, + AssemblerPredicate<(all_of FeatureZ180), "Z180 ops">; +def HaveEZ80Ops : Predicate<"Subtarget->hasEZ80Ops()">, + AssemblerPredicate<(all_of FeatureEZ80), "eZ80 ops">; +def HaveIdxHalf : Predicate<"Subtarget->hasIndexHalfRegs()">, + AssemblerPredicate<(all_of FeatureIdxHalf), + "index half regs">; + +//===----------------------------------------------------------------------===// +// Z80 Instruction Format Definitions. +// + +include "Z80InstrFormats.td" + +//===----------------------------------------------------------------------===// +// Pattern fragments. +// + +// Z80 specific condition code. These correspond to CondCode in +// Z80InstrInfo.h. They must be kept in synch. +def Z80_COND_NZ : PatLeaf<(i8 0)>; +def Z80_COND_Z : PatLeaf<(i8 1)>; +def Z80_COND_NC : PatLeaf<(i8 2)>; +def Z80_COND_C : PatLeaf<(i8 3)>; +def Z80_COND_PO : PatLeaf<(i8 4)>; +def Z80_COND_PE : PatLeaf<(i8 5)>; +def Z80_COND_P : PatLeaf<(i8 6)>; +def Z80_COND_M : PatLeaf<(i8 7)>; + +//===----------------------------------------------------------------------===// +// Z80 Operand Definitions. +// + +def aptr_rc : PointerLikeRegClass<1>; +def iptr_rc : PointerLikeRegClass<2>; + +def mem : Operand { + let PrintMethod = "printMem"; + let MIOperandInfo = (ops imm); + let OperandType = "OPERAND_MEMORY"; +} +def ptr : Operand { + let PrintMethod = "printPtr"; + let MIOperandInfo = (ops aptr_rc); + let OperandType = "OPERAND_MEMORY"; +} +def off : Operand { + let PrintMethod = "printOff"; + let MIOperandInfo = (ops iptr_rc, i8imm); + let OperandType = "OPERAND_MEMORY"; +} +def off16 : Operand { + let PrintMethod = "printAddr"; + let MIOperandInfo = (ops I16, i8imm); +} +def off24 : Operand { + let PrintMethod = "printAddr"; + let MIOperandInfo = (ops I24, i8imm); +} +def bitimm : Operand, PatLeaf<(i8 imm), [{ + return N->getZExtValue() < 8; +}]> { + let PrintMethod = "printBit"; +} + +let OperandType = "OPERAND_IMMEDIATE" in def i24imm : Operand; + +def jmptarget : Operand; +def jmptargetoff : Operand; + +def cc : Operand { + let PrintMethod = "printCCOperand"; +} + +//===----------------------------------------------------------------------===// +// Pattern Fragments. +// +def imm_long_XFORM : SDNodeXFormgetTargetConstant(N->getZExtValue() & 0xffffff, + SDLoc(N), MVT::i24); +}]>; +def imm_top_XFORM : SDNodeXFormgetTargetConstant(N->getZExtValue() >> 24, SDLoc(N), MVT::i8); +}]>; + +//===----------------------------------------------------------------------===// +// Z80 Complex Pattern Definitions. +// +def mempat : ComplexPattern; +def offpat : ComplexPattern; + +def gi_mempat : + GIComplexOperandMatcher, + GIComplexPatternEquiv; +def gi_offpat : + GIComplexOperandMatcher, + GIComplexPatternEquiv; + +//===----------------------------------------------------------------------===// +// Instruction list. +// + +let hasPostISelHook = 1 in { + let Defs = [SPS], Uses = [SPS] in { + def ADJCALLSTACKDOWN16 : P<(outs), + (ins i16imm:$amt1, i16imm:$amt2, i16imm:$amt3)>, + Requires<[In16BitMode]>; + def ADJCALLSTACKUP16 : P<(outs), (ins i16imm:$amt1, i16imm:$amt2)>, + Requires<[In16BitMode]>; + } + let Defs = [SPL], Uses = [SPL] in { + def ADJCALLSTACKDOWN24 : P<(outs), + (ins i24imm:$amt1, i24imm:$amt2, i24imm:$amt3)>, + Requires<[In24BitMode]>; + def ADJCALLSTACKUP24 : P<(outs), (ins i24imm:$amt1, i24imm:$amt2)>, + Requires<[In24BitMode]>; + } +} +let usesCustomInserter = 1 in { + let Uses = [F] in { + def SetCC : P<(outs R8:$dst), (ins i8imm:$cc)>; + def Select8 : P<(outs R8:$dst), (ins R8:$true, R8:$false, i8imm:$cc), + [(set R8:$dst, (Z80select R8:$true, R8:$false, imm:$cc, + F))]>; + def Select16 : P<(outs R16:$dst), (ins R16:$true, R16:$false, i8imm:$cc), + [(set R16:$dst, (Z80select R16:$true, R16:$false, imm:$cc, + F))]>; + def Select24 : P<(outs R24:$dst), (ins R24:$true, R24:$false, i8imm:$cc), + [(set R24:$dst, (Z80select R24:$true, R24:$false, imm:$cc, + F))]>, + Requires<[In24BitMode]>; + } + let Uses = [F] in { + let Defs = [A, F] in + def SExt8 : P<(outs), (ins),[(set A, (Z80sext F))]>; + let Defs = [HL, F] in + def SExt16 : P<(outs), (ins), [(set HL, (Z80sext F))]>; + let Defs = [UHL, F] in + def SExt24 : P<(outs), (ins), [(set UHL, (Z80sext F))]>; + } +} + +let hasSideEffects = 0 in +def NOP : I; +def DI : I; +def EI : I; + +//===----------------------------------------------------------------------===// +// Control Flow Instructions. +// + +// All calls clobber the non-callee saved registers. SP is marked as a use to +// prevent stack-pointer assignments that appear immediately before calls from +// potentially appearing dead. Uses for argument registers are added manually. +let isCall = 1 in { + let Uses = [SPS] in { + def CALL16 : I16i, + Requires<[In16BitMode]>; + def CALL16r : P <(outs), (ins A16:$tgt), [(Z80call A16:$tgt)]>, + Requires<[In16BitMode]>; + } + let Uses = [F, SPS] in + def CALL16CC : I16i; + let Uses = [SPL] in { + def CALL24 : I24i; + def CALL24r : P <(outs), (ins A24:$tgt), [(Z80call A24:$tgt)]>; + } + let Uses = [F, SPL] in + def CALL24CC : I24i; +} + +let isTerminator = 1, isReturn = 1, isBarrier = 1 in { + let Defs = [SPS], Uses = [SPS] in { + def RET16 : I16; + def RETN16 : I16; + def RETI16 : I16; + } + let Defs = [SPS], Uses = [F, SPS] in + def RET16CC : I16; + let Defs = [SPL], Uses = [SPL] in { + def RET24 : I24; + def RETN24 : I24; + def RETI24 : I24; + } + let Defs = [SPL], Uses = [F, SPL] in + def RET24CC : I24; + def EI_RETI : P<(outs), (ins), [(Z80retiflag)]>; +} +let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1 in { + let Uses = [SPS] in { + def TCRETURN16 : P<(outs), (ins i16imm:$tgt), [(Z80tcret mempat:$tgt)]>, + Requires<[In16BitMode]>; + def TCRETURN16r : P<(outs), (ins A16:$tgt), [(Z80tcret A16:$tgt)]>, + Requires<[In16BitMode]>; + } + let Uses = [F, SPS] in + def TCRETURN16CC : P<(outs), (ins i16imm:$tgt, cc:$cc)>, + Requires<[In16BitMode]>; + let Uses = [SPL] in { + def TCRETURN24 : P<(outs), (ins i24imm:$tgt), [(Z80tcret mempat:$tgt)]>, + Requires<[In24BitMode]>; + def TCRETURN24r : P<(outs), (ins A24:$tgt), [(Z80tcret A24:$tgt)]>, + Requires<[In24BitMode]>; + } + let Uses = [F, SPL] in + def TCRETURN24CC : P<(outs), (ins i24imm:$tgt, cc:$cc)>, + Requires<[In24BitMode]>; +} + +let isBranch = 1, isTerminator = 1 in { + let isBarrier = 1 in { + def JQ : Pseudo<"jq", "\t$tgt", "", (outs), (ins jmptarget:$tgt), + [(br bb:$tgt)]>; + def JR : Io ; + def JP16 : I16i; + def JP24 : I24i; + let isIndirectBranch = 1 in { + def JP16r : I16; + def JP24r : I24; + } + } + let Uses = [F] in { + def JQCC : Pseudo<"jq", "\t$cc, $tgt", "", + (outs), (ins jmptarget:$tgt, cc:$cc), + [(Z80brcond bb:$tgt, imm:$cc, F)]>; + def JRCC : Io ; + def JP16CC : I16i; + def JP24CC : I24i; + } +} + +//===----------------------------------------------------------------------===// +// Load Instructions. +// + +def LD8gg : I; +def LD8xx : I, Requires<[HaveIdxHalf]>; +def LD8yy : I, Requires<[HaveIdxHalf]>; + +let isMoveImm = 1, isReMaterializable = 1 in { + let Defs = [F] in { + def LD8r0 : Pseudo<"ld", "\t$dst, 0", "", + (outs R8:$dst), (ins), [(set R8:$dst, 0)]>; + def LD24r0 : Pseudo<"ld", "\t$dst, 0", "", + (outs R24:$dst), (ins), [(set R24:$dst, 0)]>; + def LD24r_1 : Pseudo<"ld", "\t$dst, -1", "", + (outs R24:$dst), (ins), [(set R24:$dst, -1)]>; + } + def LD8ri : I8i ; + def LD16ri : I16i; + def LD24ri : I24i; +} + +let mayLoad = 1, canFoldAsLoad = 1, isReMaterializable = 1 in { + let Defs = [A] in + def LD8am : I8i ; + def LD16rm : Pseudo< "ld", "\t$dst, $src", "", + (outs R16:$dst), (ins mem:$src), + [(set R16:$dst, (load mempat:$src))]>; + def LD24rm : Pseudo< "ld", "\t$dst, $src", "", + (outs R24:$dst), (ins mem:$src), + [(set R24:$dst, (load mempat:$src))]>; + def LD16am : I16i ; + def LD24am : I24i ; + def LD16om : I16i ; + def LD24om : I24i ; + + def LD8rp : Pseudo< "ld", "\t$dst, $src", "", + (outs R8:$dst), (ins ptr:$src)>; + def LD8gp : I ; + def LD16rp : I16 , + Requires<[HaveEZ80Ops]>; + def LD88rp : Pseudo< "ld", "\t$dst, $src", "", + (outs R16:$dst), (ins ptr:$src), + [(set R16:$dst, (load iPTR:$src))]>; + def LD24rp : I24 ; + + def LD8ro : Pseudo< "ld", "\t$dst, $src", "", + (outs R8:$dst), (ins off:$src)>; + def LD8go : Io ; + def LD16ro : I16o , + Requires<[HaveEZ80Ops]>; + def LD88ro : Pseudo< "ld", "\t$dst, $src", "", + (outs R16:$dst), (ins off:$src), + [(set R16:$dst, (load offpat:$src))]>; + def LD24ro : I24o ; +} +def : Pat<(i16 (extloadi8 mempat:$src)), (LD16rm mem:$src)>; +def : Pat<(i16 (extloadi8 iPTR:$src)), + (INSERT_SUBREG (IMPLICIT_DEF), (LD8rp ptr:$src), sub_low)>; +def : Pat<(i16 (extloadi8 offpat:$src)), + (INSERT_SUBREG (IMPLICIT_DEF), (LD8ro off:$src), sub_low)>; +def : Pat<(i24 (extloadi8 mempat:$src)), (LD24rm mem:$src)>; +def : Pat<(i24 (extloadi8 iPTR:$src)), + (INSERT_SUBREG (IMPLICIT_DEF), (LD8rp ptr:$src), sub_low)>; +def : Pat<(i24 (extloadi8 offpat:$src)), + (INSERT_SUBREG (IMPLICIT_DEF), (LD8ro off:$src), sub_low)>; +def : Pat<(i24 (extloadi16 mempat:$src)), (LD24rm mem:$src)>; +def : Pat<(i24 (extloadi16 iPTR:$src)), (LD24rp ptr:$src)>; +def : Pat<(i24 (extloadi16 offpat:$src)), (LD24ro off:$src)>; + +let mayStore = 1 in { + let Uses = [A] in + def LD8ma : I8i < NoPre, 0x32, "ld", "\t$dst, a", "", + (outs), (ins mem:$dst), [(store A, mempat:$dst)]>; + def LD16mr : Pseudo< "ld", "\t$dst, $src", "", + (outs), (ins mem:$dst, R16:$src), + [(store R16:$src, mempat:$dst)]>; + def LD24mr : Pseudo< "ld", "\t$dst, $src", "", + (outs), (ins mem:$dst, R24:$src), + [(store R24:$src, mempat:$dst)]>; + def LD16ma : I16i ; + def LD24ma : I24i ; + def LD16mo : I16i < EDPre, 0x43, "ld", "\t$dst, $src", "", + (outs), (ins mem:$dst, O16:$src)>; + def LD24mo : I24i < EDPre, 0x43, "ld", "\t$dst, $src", "", + (outs), (ins mem:$dst, O24:$src)>; + + def LD8pr : Pseudo< "ld", "\t$dst, $src", "", + (outs), (ins ptr:$dst, R8:$src)>; + def LD8pg : I < NoPre, 0x70, "ld", "\t$dst, $src", "", + (outs), (ins ptr:$dst, G8:$src), + [(store G8:$src, iPTR:$dst)]>; + def LD16pr : I16 < EDPre, 0x0F, "ld", "\t$dst, $src", "", + (outs), (ins ptr:$dst, R16:$src), + [(store R16:$src, iPTR:$dst)]>, + Requires<[In16BitMode, HaveEZ80Ops]>; + def LD88pr : Pseudo< "ld", "\t$dst, $src", "", + (outs), (ins ptr:$dst, R16:$src), + [(store R16:$src, iPTR:$dst)]>; + def LD24pr : I24 < EDPre, 0x0F, "ld", "\t$dst, $src", "", + (outs), (ins ptr:$dst, R24:$src), + [(store R24:$src, iPTR:$dst)]>; + + def LD8or : Pseudo< "ld", "\t$dst, $src", "", + (outs), (ins off:$dst, R8:$src)>; + def LD8og : Io ; + def LD16or : I16o , + Requires<[HaveEZ80Ops]>; +} +// LD16/24or encodes R16/24:$src, but there aren't enough index registers for +// regalloc to use it, since one of the index registers is reserved as the frame +// pointer and the other is used in offpat:$dst, so just pattern match on +// G16/24:$src. +def : Pat<(store G16:$src, offpat:$dst), (LD16or off:$dst, R16:$src)>, + Requires<[In16BitMode, HaveEZ80Ops]>; +let mayStore = 1 in { + def LD88or : Pseudo< "ld", "\t$dst, $src", "", + (outs), (ins off:$dst, R16:$src), + [(store R16:$src, offpat:$dst)]>; + def LD24or : I24o ; +} +// Same as above. +def : Pat<(store G24:$src, offpat:$dst), (LD24or off:$dst, R24:$src)>; + +let mayStore = 1 in { + def LD8pi : I8oi < NoPre, 0x36, "ld", "\t$dst, $src", "", + (outs), (ins ptr:$dst, i8imm:$src), + [(store (i8 imm:$src), iPTR:$dst)]>; + def LD8oi : I8oi ; +} + +let Defs = [SPS] in +def LD16SP : I16; +let Defs = [SPL] in +def LD24SP : I24; + +def EXAF : I; +def EXX : I; + +let Defs = [DE, HL], Uses = [DE, HL] in +def EX16DE : I16; +let Defs = [UDE, UHL], Uses = [UDE, UHL] in +def EX24DE : I24; + +let Constraints = "$imp = $dst" in { +let Uses = [SPS] in +def EX16SP : I16; +let Uses = [SPL] in +def EX24SP : I24; +} + +let Defs = [SPS], Uses = [SPS] in { + let mayLoad = 1 in + def POP16r : I16 , + Requires<[In16BitMode]>; + let mayStore = 1 in { + def PUSH16r : I16 , + Requires<[In16BitMode]>; + def PEA16o : I16o, + Requires<[In16BitMode, HaveEZ80Ops]>; + } +} +let Defs = [AF, SPS], Uses = [SPS], mayLoad = 1 in +def POP16AF : I16, + Requires<[In16BitMode]>; +let Defs = [SPS], Uses = [AF, SPS], mayStore = 1 in +def PUSH16AF : I16, + Requires<[In16BitMode]>; +let Defs = [SPL], Uses = [SPL] in { + let mayLoad = 1 in + def POP24r : I24 ; + let mayStore = 1 in { + def PUSH24r : I24 ; + def PEA24o : I24o; + } +} +let Defs = [AF, SPL], Uses = [SPL], mayLoad = 1 in +def POP24AF : I24; +let Defs = [SPL], Uses = [AF, SPL], mayStore = 1 in +def PUSH24AF : I24; + +let isReMaterializable = 1, Defs = [F] in { + def RCF : P; + def SCF : I; + def CCF : I; +} + +//===----------------------------------------------------------------------===// +// Arithmetic Instructions. +// + +let Defs = [A, F], Uses = [A] in { + def CPL : I; + def NEG : I; + def RLCA : I; + def RRCA : I; +} + +let Defs = [A, F], Uses = [A, F] in { + def RLA : I; + def RRA : I; +} + +let Defs = [F] in +multiclass UnOp8RF opcode, string mnemonic, + Z80RC8 rc8 = !if(!eq(prefix.Value, CBPre.Value), G8, R8)> { + def 8r : I (!strconcat("Z80", mnemonic, "_flag")) + rc8:$imp))]>; + let mayLoad = 1, mayStore = 1 in { + def 8p : I (!strconcat("Z80", mnemonic, "_flag")) + (i8 (load iPTR:$adr))), iPTR:$adr), + (implicit F)]>; + def 8o : Io(!strconcat("Z80", mnemonic, "_flag")) + (i8 (load offpat:$adr))), offpat:$adr), + (implicit F)]>; + } +} +let Defs = [F], Uses = [F] in +multiclass UnOp8RFF opcode, string mnemonic, + Z80RC8 rc8 = !if(!eq(prefix.Value, CBPre.Value), G8, R8)> { + def 8r : I (!strconcat("Z80", mnemonic, "_flag")) + rc8:$imp, F))]>; + let mayLoad = 1, mayStore = 1 in { + def 8p : I (!strconcat("Z80", mnemonic, "_flag")) + (i8 (load iPTR:$adr)), F), iPTR:$adr), + (implicit F)]>; + def 8o : Io(!strconcat("Z80", mnemonic, "_flag")) + (i8 (load offpat:$adr)), F), offpat:$adr), + (implicit F)]>; + } +} +multiclass BinOp8RF opcode, string mnemonic, + bit compare = 0> { + let isCompare = compare, Defs = [A, F], Uses = [A] in { + def 8ar : I (!strconcat("Z80", mnemonic, "_flag")) + A, R8:$src))]>; + def 8ai : Ii(!strconcat("Z80", mnemonic, "_flag")) + A, imm:$src))]>; + let mayLoad = 1 in { + def 8ap : I (!strconcat("Z80", mnemonic, "_flag")) + A, (i8 (load iPTR:$src))))]>; + def 8ao : Io(!strconcat("Z80", mnemonic, "_flag")) + A, (i8 (load offpat:$src))))]>; + } + } + def : Pat<(!cast(mnemonic) A, R8:$src), + (!cast(!strconcat(NAME, "8ar")) R8:$src)>; + def : Pat<(!cast(mnemonic) A, imm:$src), + (!cast(!strconcat(NAME, "8ai")) imm:$src)>; + def : Pat<(!cast(mnemonic) A, (load iPTR:$src)), + (!cast(!strconcat(NAME, "8ap")) ptr:$src)>; + def : Pat<(!cast(mnemonic) A, (load offpat:$src)), + (!cast(!strconcat(NAME, "8ao")) off:$src)>; +} +multiclass BinOp8RFF opcode, string mnemonic, + SDNode node, bit compare = 0> { + let isCompare = compare, Defs = [A, F], Uses = [A, F] in { + def 8ar : I (!strconcat("Z80", mnemonic, "_flag")) + A, R8:$src, F))]>; + def 8ai : Ii(!strconcat("Z80", mnemonic, "_flag")) + A, imm:$src, F))]>; + let mayLoad = 1 in { + def 8ap : I (!strconcat("Z80", mnemonic, "_flag")) + A, (i8 (load iPTR:$src)), F))]>; + def 8ao : Io(!strconcat("Z80", mnemonic, "_flag")) + A, (i8 (load offpat:$src)), F))]>; + } + } + def : Pat<(node A, R8:$src), + (!cast(!strconcat(NAME, "8ar")) R8:$src)>; + def : Pat<(node A, imm:$src), + (!cast(!strconcat(NAME, "8ai")) imm:$src)>; + def : Pat<(node A, (load iPTR:$src)), + (!cast(!strconcat(NAME, "8ap")) ptr:$src)>; + def : Pat<(node A, (load offpat:$src)), + (!cast(!strconcat(NAME, "8ao")) off:$src)>; +} +multiclass BinOp8F opcode, string mnemonic, + bit compare = 0> { + let isCompare = compare, Defs = [F], Uses = [A] in { + def 8ar : I (!strconcat("Z80", mnemonic, "_flag")) + A, R8:$src))]>; + def 8ai : Ii(!strconcat("Z80", mnemonic, "_flag")) + A, imm:$src))]>; + let mayLoad = 1 in { + def 8ap : I (!strconcat("Z80", mnemonic, "_flag")) + A, (i8 (load iPTR:$src))))]>; + def 8ao : Io(!strconcat("Z80", mnemonic, "_flag")) + A, (i8 (load offpat:$src))))]>; + } + } +} +let Defs = [F] in +multiclass BitOp8F opcode, string mnemonic> { + def 8bg : I < CBPre, {opcode, 0b000, 0b000}, mnemonic, "\t$bit, $src", "", + (outs), (ins bitimm:$bit, G8:$src), + [(set F, (!cast(!strconcat("Z80", mnemonic, "_flag")) + bitimm:$bit, G8:$src))]>; + let mayLoad = 1 in { + def 8bp : I < CBPre, {opcode, 0b000, 0b110}, mnemonic, "\t$bit, $adr", "", + (outs), (ins bitimm:$bit, ptr:$adr), + [(set F, (!cast(!strconcat("Z80", mnemonic, "_flag")) + bitimm:$bit, (i8 (load iPTR:$adr))))]>; + def 8bo : Io(!strconcat("Z80", mnemonic, "_flag")) + bitimm:$bit, (i8 (load offpat:$adr))))]>; + } +} +multiclass BitOp8R opcode, string mnemonic> { + def 8bg : I < CBPre, {opcode, 0b000, 0b000}, mnemonic, "\t$bit, $dst", + "$imp = $dst", (outs G8:$dst), (ins bitimm:$bit, G8:$imp), + [(set G8:$dst, + (!cast(!strconcat("Z80", mnemonic, "_flag")) + bitimm:$bit, G8:$imp))]>; + let mayLoad = 1, mayStore = 1 in { + def 8bp : I < CBPre, {opcode, 0b000, 0b110}, mnemonic, "\t$bit, $adr", "", + (outs), (ins bitimm:$bit, ptr:$adr), + [(store (!cast(!strconcat("Z80", mnemonic, "_flag")) + bitimm:$bit, (i8 (load iPTR:$adr))), + iPTR:$adr), + (implicit F)]>; + def 8bo : Io(!strconcat("Z80", mnemonic, "_flag")) + bitimm:$bit, (i8 (load offpat:$adr))), + offpat:$adr), + (implicit F)]>; + } +} + +defm RLC : UnOp8RF ; +defm RRC : UnOp8RF ; +defm RL : UnOp8RFF ; +defm RR : UnOp8RFF ; +defm SLA : UnOp8RF ; +defm SRA : UnOp8RF ; +defm SRL : UnOp8RF ; +defm BIT : BitOp8F < 1, "bit">; +defm RES : BitOp8R < 2, "res">; +defm SET : BitOp8R < 3, "set">; +defm INC : UnOp8RF ; +defm DEC : UnOp8RF ; +defm ADD : BinOp8RF ; +defm ADC : BinOp8RFF; +defm SUB : BinOp8RF ; +defm SBC : BinOp8RFF; +defm AND : BinOp8RF ; +defm XOR : BinOp8RF ; +defm OR : BinOp8RF ; +defm CP : BinOp8F ; +defm TST : BinOp8F , + Requires<[HaveEZ80Ops]>; + +def : Pat<(add R8:$reg, 1), (INC8r R8:$reg)>; +def : Pat<(add R8:$reg, -1), (DEC8r R8:$reg)>; + +def INC16r : I16; +def DEC16r : I16; +def INC24r : I24; +def DEC24r : I24; +let Defs = [SPS], Uses = [SPS] in { +def INC16SP : I16; +def DEC16SP : I16; +} +let Defs = [SPL], Uses = [SPL] in { +def INC24SP : I24; +def DEC24SP : I24; +} +def : Pat<(add R16:$imp, 1), (INC16r R16:$imp)>; +def : Pat<(add R16:$imp, -1), (DEC16r R16:$imp)>; +def : Pat<(add R24:$imp, 1), (INC24r R24:$imp)>; +def : Pat<(add R24:$imp, -1), (DEC24r R24:$imp)>; + +let Defs = [F] in { + def ADD16aa : I16; + def ADD24aa : I24; + def ADD16ao : I16; + def ADD24ao : I24; + let Uses = [SPS] in + def ADD16SP : I16; + let Uses = [SPL] in + def ADD24SP : I24; +} +def : Pat<(add A16:$imp, A16:$imp), (ADD16aa A16:$imp)>; +def : Pat<(add A24:$imp, A24:$imp), (ADD24aa A24:$imp)>; +def : Pat<(addc A16:$imp, A16:$imp), (ADD16aa A16:$imp)>; +def : Pat<(addc A24:$imp, A24:$imp), (ADD24aa A24:$imp)>; +def : Pat<(add A16:$imp, O16:$src), (ADD16ao A16:$imp, O16:$src)>; +def : Pat<(add A24:$imp, O24:$src), (ADD24ao A24:$imp, O24:$src)>; +def : Pat<(addc A16:$imp, O16:$src), (ADD16ao A16:$imp, O16:$src)>; +def : Pat<(addc A24:$imp, O24:$src), (ADD24ao A24:$imp, O24:$src)>; + +let Defs = [HL, F] in { + let Uses = [HL, F] in { + def SBC16aa : I16; + def ADC16aa : I16; + def SBC16ao : I16; + def ADC16ao : I16; + } + let Uses = [HL, SPS, F] in { + def SBC16SP : I16; + def ADC16SP : I16; + } +} +let Defs = [UHL, F] in { + let Uses = [UHL, F] in { + def SBC24aa : I24; + def ADC24aa : I24; + def SBC24ao : I24; + def ADC24ao : I24; + } + let Uses = [UHL, SPL, F] in { + def SBC24SP : I24; + def ADC24SP : I24; + } +} +def : Pat<(sube HL, O16:$src), (SBC16ao O16:$src)>; +def : Pat<(adde HL, O16:$src), (ADC16ao O16:$src)>; +def : Pat<(sube UHL, O24:$src), (SBC24ao O24:$src)>; +def : Pat<(adde UHL, O24:$src), (ADC24ao O24:$src)>; + +let Defs = [HL, F], Uses = [HL] in { + def SUB16ao : P<(outs), (ins O16:$src), + [(set HL, F, (Z80sub_flag HL, O16:$src))]>; +} +let Defs = [UHL, F], Uses = [UHL] in { + def SUB24ao : P<(outs), (ins O24:$src), + [(set UHL, F, (Z80sub_flag UHL, O24:$src))]>, + Requires<[HaveEZ80Ops]>; +} +let Defs = [F] in { + let Uses = [HL] in { + def CP16a0 : P<(outs), (ins), [(set F, (Z80cp_flag HL, 0))]>; + def CP16ao : P<(outs), (ins O16:$src), + [(set F, (Z80cp_flag HL, O16:$src))]>; + } + let Uses = [UHL] in { + def CP24a0 : P<(outs), (ins), [(set F, (Z80cp_flag UHL, 0))]>, + Requires<[HaveEZ80Ops]>; + def CP24ao : P<(outs), (ins O24:$src), + [(set F, (Z80cp_flag UHL, O24:$src))]>, + Requires<[HaveEZ80Ops]>; + } +} +def : Pat<(sub HL, O16:$src), (SUB16ao O16:$src)>; +def : Pat<(sub UHL, O24:$src), (SUB24ao O24:$src)>; +def : Pat<(subc HL, O16:$src), (SUB16ao O16:$src)>; +def : Pat<(subc UHL, O24:$src), (SUB24ao O24:$src)>; + +def LEA16ro : I16o, Requires<[HaveEZ80Ops]>; +def LEA24ro : I24o; + +def MLT8rr : I, Requires<[HaveZ180Ops]>; +def : Pat<(i8 (mul R8:$op1, R8:$op2)), + (EXTRACT_SUBREG (MLT8rr (REG_SEQUENCE G16, R8:$op1, sub_high, + R8:$op2, sub_low)), + sub_low)>; +def : Pat<(i16 (mul (zext R8:$op1), (zext R8:$op2))), + (MLT8rr (REG_SEQUENCE G16, R8:$op1, sub_high, R8:$op2, sub_low))>; + +//===----------------------------------------------------------------------===// +// Block Instructions. +//===----------------------------------------------------------------------===// +multiclass Block16 opcode, string mnemonic, + string mnemonicr = mnemonic> { + def I16 : I16; + def D16 : I16; + def IR16 : I16; + def DR16 : I16; +} +multiclass Block24 opcode, string mnemonic, + string mnemonicr = mnemonic> { + def I24 : I24; + def D24 : I24; + def IR24 : I24; + def DR24 : I24; +} + +let Defs = [DE, HL, BC, F], Uses = [DE, HL, BC], mayLoad = 1, mayStore = 1 in +defm LD : Block16<0, "ld">; +let Defs = [DE, HL, BC, F], Uses = [DE, HL, BC], mayLoad = 1 in +defm CP : Block16<1, "cp">; +let Defs = [HL, C, B, F], Uses = [HL, C, B] in { + let mayStore = 1 in defm IN : Block16<2, "in">; + let mayLoad = 1 in defm OUT : Block16<3, "out", "ot">; +} + +let Defs = [UDE, UHL, UBC, F], Uses = [UDE, UHL, UBC], + mayLoad = 1, mayStore = 1 in +defm LD : Block24<0, "ld">; +let Defs = [UHL, UBC, F], Uses = [UHL, UBC, A], mayLoad = 1 in +defm CP : Block24<1, "cp">; +let Defs = [UHL, C, B, F], Uses = [UHL, C, B] in { + let mayStore = 1 in defm IN : Block24<2, "in">; + let mayLoad = 1 in defm OUT : Block24<3, "out", "ot">; +} + +let usesCustomInserter = 1, Defs = [F], Uses = [F], + mayLoad = 1, mayStore = 1 in { + def LDR16 : P<(outs), (ins R16: $de, R16: $hl, R16: $bc)>; + def LDR24 : P<(outs), (ins R24:$ude, R24:$uhl, R24:$ubc)>, + Requires<[HaveEZ80Ops]>; +} + +//===----------------------------------------------------------------------===// +// Non-Instruction Patterns. +//===----------------------------------------------------------------------===// + +// addresses +def : Pat<(i16 (Z80Wrapper tglobaladdr :$src)), (LD16ri tglobaladdr :$src)>; +def : Pat<(i24 (Z80Wrapper tglobaladdr :$src)), (LD24ri tglobaladdr :$src)>; +def : Pat<(i16 (Z80Wrapper texternalsym :$src)), (LD16ri texternalsym :$src)>; +def : Pat<(i24 (Z80Wrapper texternalsym :$src)), (LD24ri texternalsym :$src)>; +def : Pat<(i16 (Z80Wrapper tblockaddress:$src)), (LD16ri tblockaddress:$src)>; +def : Pat<(i24 (Z80Wrapper tblockaddress:$src)), (LD24ri tblockaddress:$src)>; + +// calls +def : Pat<(Z80call (tglobaladdr :$dst)), (CALL16 tglobaladdr :$dst)>, + Requires<[In16BitMode]>; +def : Pat<(Z80call (tglobaladdr :$dst)), (CALL24 tglobaladdr :$dst)>, + Requires<[In24BitMode]>; +def : Pat<(Z80call (texternalsym:$dst)), (CALL16 texternalsym:$dst)>, + Requires<[In16BitMode]>; +def : Pat<(Z80call (texternalsym:$dst)), (CALL24 texternalsym:$dst)>, + Requires<[In24BitMode]>; + +def : Pat<(Z80tcret (tglobaladdr :$dst)), (TCRETURN16 tglobaladdr :$dst)>, + Requires<[In16BitMode]>; +def : Pat<(Z80tcret (tglobaladdr :$dst)), (TCRETURN24 tglobaladdr :$dst)>, + Requires<[In24BitMode]>; +def : Pat<(Z80tcret (texternalsym:$dst)), (TCRETURN16 texternalsym:$dst)>, + Requires<[In16BitMode]>; +def : Pat<(Z80tcret (texternalsym:$dst)), (TCRETURN24 texternalsym:$dst)>, + Requires<[In24BitMode]>; + +//===----------------------------------------------------------------------===// +// Subsystems. +//===----------------------------------------------------------------------===// + +// anyext +def : Pat<(i16 (anyext R8 :$src)), + (INSERT_SUBREG (IMPLICIT_DEF), R8 :$src, sub_low)>; +def : Pat<(i24 (anyext R8 :$src)), + (INSERT_SUBREG (IMPLICIT_DEF), R8 :$src, sub_low)>; +def : Pat<(i24 (anyext R16:$src)), + (INSERT_SUBREG (IMPLICIT_DEF), R16:$src, sub_short)>; + +// zext +def : Pat<(i16 (zext R8 :$src)), + (REG_SEQUENCE R16, (LD8r0), sub_high, R8:$src, sub_low)>; +def : Pat<(i24 (zext R8 :$src)), + (INSERT_SUBREG (i24 (LD24r0)), R8 :$src, sub_low)>; +def : Pat<(i24 (zext R16:$src)), + (INSERT_SUBREG (i24 (LD24r0)), R16:$src, sub_short)>; + +// trunc +def : Pat<(i8 (trunc R16:$src)), (EXTRACT_SUBREG R16:$src, sub_low)>; +def : Pat<(i8 (trunc R24:$src)), (EXTRACT_SUBREG R24:$src, sub_low)>; +def : Pat<(i16 (trunc R24:$src)), (EXTRACT_SUBREG R24:$src, sub_short)>; diff --git a/llvm/lib/Target/Z80/Z80MCInstLower.cpp b/llvm/lib/Target/Z80/Z80MCInstLower.cpp new file mode 100644 index 00000000000000..083a6b70118733 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80MCInstLower.cpp @@ -0,0 +1,114 @@ +//===-- Z80MCInstLower.cpp - Convert Z80 MachineInstr to an MCInst --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains code to lower Z80 MachineInstrs to their corresponding +// MCInst records. +// +//===----------------------------------------------------------------------===// + +#include "Z80AsmPrinter.h" +#include "llvm/IR/Mangler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Support/Debug.h" +using namespace llvm; + +#define DEBUG_TYPE "z80-mclower" + +namespace { +/// Z80MCInstLower - This class is used to lower a MachineInstr into an MCInst. +class Z80MCInstLower { + MCContext &Ctx; + Z80AsmPrinter &AsmPrinter; + +public: + Z80MCInstLower(const MachineFunction &MF, Z80AsmPrinter &AP); + + Optional LowerMachineOperand(const MachineInstr *MI, + const MachineOperand &MO) const; + + MCSymbol *GetGlobalAddressSymbol(const MachineOperand &MO) const; + MCSymbol *GetExternalSymbolSymbol(const MachineOperand &MO) const; + MCOperand LowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym) const; + + void Lower(const MachineInstr *MI, MCInst &OutMI) const; + }; +} + +Z80MCInstLower::Z80MCInstLower(const MachineFunction &MF, Z80AsmPrinter &AP) + : Ctx(MF.getContext()), AsmPrinter(AP) {} + +/// GetGlobalAddressSymbol - Lower an MO_GlobalAddress operand to an MCSymbol. +MCSymbol * +Z80MCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const { + assert(!MO.getTargetFlags() && "Unknown target flag on GV operand"); + return AsmPrinter.getSymbol(MO.getGlobal()); +} + +/// GetExternalSymbolSymbol - Lower an MO_ExternalSymbol operand to an MCSymbol. +MCSymbol * +Z80MCInstLower::GetExternalSymbolSymbol(const MachineOperand &MO) const { + assert(!MO.getTargetFlags() && "Unknown target flag on GV operand"); + return AsmPrinter.GetExternalSymbolSymbol(MO.getSymbolName()); +} + +MCOperand Z80MCInstLower::LowerSymbolOperand(const MachineOperand &MO, + MCSymbol *Sym) const { + assert(!MO.getTargetFlags() && "Unknown target flag on GV operand"); + const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Ctx); + if (!MO.isJTI() && MO.getOffset()) + Expr = MCBinaryExpr::createAdd( + Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx); + return MCOperand::createExpr(Expr); +} + +Optional +Z80MCInstLower::LowerMachineOperand(const MachineInstr *MI, + const MachineOperand &MO) const { + switch (MO.getType()) { + default: + LLVM_DEBUG(MI->dump()); + llvm_unreachable("unknown operand type"); + case MachineOperand::MO_Register: + return MCOperand::createReg(MO.getReg()); + case MachineOperand::MO_Immediate: + return MCOperand::createImm(MO.getImm()); + case MachineOperand::MO_CImmediate: + return MCOperand::createImm(MO.getCImm()->getSExtValue()); + case MachineOperand::MO_MachineBasicBlock: + return MCOperand::createExpr(MCSymbolRefExpr::create( + MO.getMBB()->getSymbol(), Ctx)); + case MachineOperand::MO_GlobalAddress: + return LowerSymbolOperand(MO, GetGlobalAddressSymbol(MO)); + case MachineOperand::MO_ExternalSymbol: + return LowerSymbolOperand(MO, GetExternalSymbolSymbol(MO)); + case MachineOperand::MO_JumpTableIndex: + return LowerSymbolOperand(MO, AsmPrinter.GetJTISymbol(MO.getIndex())); + case MachineOperand::MO_RegisterMask: + return None; // Ignore call clobbers. + } +} + +void Z80MCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) const { + OutMI.setOpcode(MI->getOpcode()); + + MCOperand MCOp; + for (const MachineOperand &MO : MI->operands()) + if (auto PossibleMCOp = LowerMachineOperand(MI, MO)) + OutMI.addOperand(*PossibleMCOp); +} + +void Z80AsmPrinter::emitInstruction(const MachineInstr *MI) { + Z80MCInstLower MCInstLowering(*MF, *this); + + MCInst TmpInst; + MCInstLowering.Lower(MI, TmpInst); + EmitToStreamer(*OutStreamer, TmpInst); +} diff --git a/llvm/lib/Target/Z80/Z80MachineFunctionInfo.h b/llvm/lib/Target/Z80/Z80MachineFunctionInfo.h new file mode 100644 index 00000000000000..1efcd9c245d175 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80MachineFunctionInfo.h @@ -0,0 +1,61 @@ +//===-- Z80MachineFunctionInfo.h - Z80 machine function info ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares Z80-specific per-machine-function information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80MACHINEFUNCTIONINFO_H +#define LLVM_LIB_TARGET_Z80_Z80MACHINEFUNCTIONINFO_H + +#include "llvm/CodeGen/MachineFunction.h" + +namespace llvm { + +/// Z80MachineFunctionInfo - This class is derived from MachineFunction and +/// contains private Z80 target-specific information for each MachineFunction. +class Z80MachineFunctionInfo : public MachineFunctionInfo { + /// CalleeSavedFrameSize - Size of the callee-saved register portion of the + /// stack frame in bytes. + unsigned CalleeSavedFrameSize = 0; + + /// VarArgsFrameIndex - FrameIndex for start of varargs area. + int VarArgsFrameIndex = 0; + + /// SRetReturnReg - Some subtargets require that sret lowering includes + /// returning the value of the returned struct in a register. This field + /// holds the virtual register into which the sret argument is passed. + Register SRetReturnReg = 0; + + /// HasIllegalLEA - We use LEA to materialize a frame index address even if it + /// is illegal. Remember when if we do, to ensure a scavenging frame index is + /// created. + bool HasIllegalLEA = false; + +public: + Z80MachineFunctionInfo() = default; + + explicit Z80MachineFunctionInfo(MachineFunction &MF) {} + + unsigned getCalleeSavedFrameSize() const { return CalleeSavedFrameSize; } + void setCalleeSavedFrameSize(unsigned Bytes) { CalleeSavedFrameSize = Bytes; } + + int getVarArgsFrameIndex() const { return VarArgsFrameIndex; } + void setVarArgsFrameIndex(int Idx) { VarArgsFrameIndex = Idx; } + + Register getSRetReturnReg() const { return SRetReturnReg; } + void setSRetReturnReg(Register Reg) { SRetReturnReg = Reg; } + + bool getHasIllegalLEA() const { return HasIllegalLEA; } + void setHasIllegalLEA(bool V = true) { HasIllegalLEA = V; } +}; + +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/Z80/Z80RegisterInfo.cpp b/llvm/lib/Target/Z80/Z80RegisterInfo.cpp new file mode 100644 index 00000000000000..4eab54d6717d62 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80RegisterInfo.cpp @@ -0,0 +1,399 @@ +//===-- Z80RegisterInfo.cpp - Z80 Register Information --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Z80 implementation of the TargetRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#include "Z80RegisterInfo.h" +#include "Z80FrameLowering.h" +#include "Z80MachineFunctionInfo.h" +#include "Z80Subtarget.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/CodeGen/TargetFrameLowering.h" +#include "llvm/Support/Debug.h" +using namespace llvm; + +#define DEBUG_TYPE "z80reginfo" + +#define GET_REGINFO_TARGET_DESC +#include "Z80GenRegisterInfo.inc" + +Z80RegisterInfo::Z80RegisterInfo(const Triple &TT) + : Z80GenRegisterInfo(Z80::PC) { + // Cache some information. + Is24Bit = !TT.isArch16Bit() && TT.getEnvironment() != Triple::CODE16; + + // Use a callee-saved register as the base pointer. These registers must + // not conflict with any ABI requirements. + if (Is24Bit) { + SlotSize = 3; + StackPtr = Z80::SPL; + } else { + SlotSize = 2; + StackPtr = Z80::SPS; + } +} + +const TargetRegisterClass * +Z80RegisterInfo::getPointerRegClass(const MachineFunction &MF, + unsigned Kind) const { + switch (Kind) { + default: llvm_unreachable("Unexpected Kind in getPointerRegClass!"); + case 0: return Is24Bit ? &Z80::G24RegClass : &Z80::G16RegClass; + case 1: return Is24Bit ? &Z80::A24RegClass : &Z80::A16RegClass; + case 2: return Is24Bit ? &Z80::I24RegClass : &Z80::I16RegClass; + } +} + +const TargetRegisterClass * +Z80RegisterInfo::getLargestLegalSuperClass(const TargetRegisterClass *RC, + const MachineFunction &) const { + const TargetRegisterClass *Super = RC; + TargetRegisterClass::sc_iterator I = RC->getSuperClasses(); + do { + switch (Super->getID()) { + case Z80::R8RegClassID: + case Z80::R16RegClassID: + case Z80::R24RegClassID: + return Super; + } + Super = *I++; + } while (Super); + return RC; +} + +unsigned Z80RegisterInfo::getRegPressureLimit(const TargetRegisterClass *RC, + MachineFunction &MF) const { + return 3; + + switch (RC->getID()) { + default: + return 0; + case Z80::R16RegClassID: + case Z80::R24RegClassID: + return 2; + } +} + +const MCPhysReg * +Z80RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { + switch (MF->getFunction().getCallingConv()) { + default: llvm_unreachable("Unsupported calling convention"); + case CallingConv::C: + case CallingConv::Fast: + return Is24Bit ? CSR_EZ80_C_SaveList : CSR_Z80_C_SaveList; + case CallingConv::PreserveAll: + case CallingConv::Z80_LibCall: + case CallingConv::Z80_LibCall_AB: + case CallingConv::Z80_LibCall_AC: + case CallingConv::Z80_LibCall_BC: + case CallingConv::Z80_LibCall_L: + case CallingConv::Z80_LibCall_F: + return Is24Bit ? CSR_EZ80_AllRegs_SaveList : CSR_Z80_AllRegs_SaveList; + } +} + +const uint32_t * +Z80RegisterInfo::getCallPreservedMask(const MachineFunction &MF, + CallingConv::ID CC) const { + switch (CC) { + default: llvm_unreachable("Unsupported calling convention"); + case CallingConv::C: + case CallingConv::Fast: + return Is24Bit ? CSR_EZ80_C_RegMask : CSR_Z80_C_RegMask; + case CallingConv::PreserveAll: + case CallingConv::Z80_LibCall: + case CallingConv::Z80_LibCall_AB: + case CallingConv::Z80_LibCall_AC: + case CallingConv::Z80_LibCall_BC: + case CallingConv::Z80_LibCall_L: + case CallingConv::Z80_LibCall_F: + return Is24Bit ? CSR_EZ80_AllRegs_RegMask : CSR_Z80_AllRegs_RegMask; + } +} +const uint32_t *Z80RegisterInfo::getNoPreservedMask() const { + return CSR_NoRegs_RegMask; +} + +BitVector Z80RegisterInfo::getReservedRegs(const MachineFunction &MF) const { + BitVector Reserved(getNumRegs()); + + // Set the stack-pointer registers as reserved. + Reserved.set(Z80::SPS); + Reserved.set(Z80::SPL); + + // Set the program-counter register as reserved. + Reserved.set(Z80::PC); + + // Set the frame-pointer register and its aliases as reserved if needed. + for (Register Reg : + {Register(Is24Bit ? Z80::UIX : Z80::IX), getFrameRegister(MF)}) + for (MCRegAliasIterator I(Reg, this, /*IncludeSelf=*/true); I.isValid(); + ++I) + Reserved.set(*I); + + return Reserved; +} + +bool Z80RegisterInfo::saveScavengerRegister(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + MachineBasicBlock::iterator &UseMI, + const TargetRegisterClass *RC, + Register Reg) const { + return false; + const Z80Subtarget &STI = MBB.getParent()->getSubtarget(); + const TargetInstrInfo &TII = *STI.getInstrInfo(); + DebugLoc DL; + if (Reg == Z80::AF) + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)); + else + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(Reg); + for (MachineBasicBlock::iterator II = MI; II != UseMI; ++II) { + if (II->isDebugValue()) + continue; + if (II->modifiesRegister(Reg, this)) + UseMI = II; + } + if (Reg == Z80::AF) + BuildMI(MBB, UseMI, DL, TII.get(Is24Bit ? Z80::POP24AF : Z80::POP16AF)); + else + BuildMI(MBB, UseMI, DL, TII.get(Is24Bit ? Z80::POP24r : Z80::POP16r), Reg); + return true; +} + +void Z80RegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, + int SPAdj, unsigned FIOperandNum, + RegScavenger *RS) const { + MachineInstr &MI = *II; + unsigned Opc = MI.getOpcode(); + MachineBasicBlock &MBB = *MI.getParent(); + MachineFunction &MF = *MBB.getParent(); + const Z80Subtarget &STI = MF.getSubtarget(); + const Z80InstrInfo &TII = *STI.getInstrInfo(); + const Z80FrameLowering *TFI = getFrameLowering(MF); + DebugLoc DL = MI.getDebugLoc(); + int FrameIndex = MI.getOperand(FIOperandNum).getIndex(); + Register BasePtr = getFrameRegister(MF); + LLVM_DEBUG(MF.dump(); II->dump(); + dbgs() << MF.getFunction().arg_size() << '\n'); + assert(TFI->hasFP(MF) && "Stack slot use without fp unimplemented"); + int BaseOff = MF.getFrameInfo().getObjectOffset(FrameIndex); + int SlotSize = Is24Bit ? 3 : 2; + // Skip any saved callee saved registers + if (TFI->hasFP(MF)) + BaseOff += SlotSize; + // Skip return address for arguments + if (FrameIndex < 0) + BaseOff += SlotSize; + int Off = BaseOff + getFrameIndexInstrOffset(&MI, FIOperandNum); + if (isFrameOffsetLegal(&MI, BasePtr, BaseOff) && + (Opc != Z80::LEA16ro || STI.hasEZ80Ops())) { + MI.getOperand(FIOperandNum).ChangeToRegister(BasePtr, false); + MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Off); + return; + } + bool SaveFlags = RS->isRegUsed(Z80::F); + Register OffReg = RS->scavengeRegister( + Is24Bit ? &Z80::O24RegClass : &Z80::O16RegClass, II, SPAdj); + if ((Opc == Z80::LEA24ro && + Z80::A24RegClass.contains(MI.getOperand(0).getReg())) || + (Opc == Z80::LEA16ro && + Z80::A16RegClass.contains(MI.getOperand(0).getReg()))) { + Register Op0Reg = MI.getOperand(0).getReg(); + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::LD24ri : Z80::LD16ri), OffReg) + .addImm(Off); + TII.copyPhysReg(MBB, II, DL, Op0Reg, BasePtr); + if (SaveFlags) + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)); + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::ADD24ao : Z80::ADD16ao), Op0Reg) + .addReg(Op0Reg).addReg(OffReg, RegState::Kill) + ->addRegisterDead(Z80::F, this); + if (SaveFlags) + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::POP24AF : Z80::POP16AF)); + MI.eraseFromParent(); + } else if (Register ScratchReg = RS->FindUnusedReg( + Is24Bit ? &Z80::A24RegClass : &Z80::A16RegClass)) { + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::LD24ri : Z80::LD16ri), OffReg) + .addImm(Off); + TII.copyPhysReg(MBB, II, DL, ScratchReg, BasePtr); + if (SaveFlags) + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)); + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::ADD24ao : Z80::ADD16ao), + ScratchReg).addReg(ScratchReg).addReg(OffReg, RegState::Kill) + ->addRegisterDead(Z80::F, this); + if (SaveFlags) + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::POP24AF : Z80::POP16AF)); + MI.getOperand(FIOperandNum).ChangeToRegister(ScratchReg, false); + if ((Is24Bit ? Z80::I24RegClass : Z80::I16RegClass).contains(ScratchReg)) + MI.getOperand(FIOperandNum + 1).ChangeToImmediate(0); + else { + switch (Opc) { + default: llvm_unreachable("Unexpected opcode!"); + case Z80::LD24ro: Opc = Z80::LD24rp; break; + case Z80::LD16ro: Opc = Z80::LD16rp; break; + case Z80::LD88ro: Opc = Z80::LD88rp; break; + case Z80::LD8ro: Opc = Z80::LD8rp; break; + case Z80::LD8go: Opc = Z80::LD8gp; break; + case Z80::LD24or: Opc = Z80::LD24pr; break; + case Z80::LD16or: Opc = Z80::LD16pr; break; + case Z80::LD88or: Opc = Z80::LD88pr; break; + case Z80::LD8or: Opc = Z80::LD8pr; break; + case Z80::LD8og: Opc = Z80::LD8pg; break; + case Z80::LD8oi: Opc = Z80::LD8pi; break; + case Z80::PEA24o: Opc = Z80::PUSH24r; break; + case Z80::PEA16o: Opc = Z80::PUSH16r; break; + case Z80::RLC8o: Opc = Z80::RLC8p; break; + case Z80::RRC8o: Opc = Z80::RRC8p; break; + case Z80::RL8o: Opc = Z80::RL8p; break; + case Z80::RR8o: Opc = Z80::RR8p; break; + case Z80::SLA8o: Opc = Z80::SLA8p; break; + case Z80::SRA8o: Opc = Z80::SRA8p; break; + case Z80::SRL8o: Opc = Z80::SRL8p; break; + case Z80::BIT8bo: Opc = Z80::BIT8bp; break; + case Z80::RES8bo: Opc = Z80::RES8bp; break; + case Z80::SET8bo: Opc = Z80::SET8bp; break; + case Z80::INC8o: Opc = Z80::INC8p; break; + case Z80::DEC8o: Opc = Z80::DEC8p; break; + case Z80::ADD8ao: Opc = Z80::ADD8ap; break; + case Z80::ADC8ao: Opc = Z80::ADC8ap; break; + case Z80::SUB8ao: Opc = Z80::SUB8ap; break; + case Z80::SBC8ao: Opc = Z80::SBC8ap; break; + case Z80::AND8ao: Opc = Z80::AND8ap; break; + case Z80::XOR8ao: Opc = Z80::XOR8ap; break; + case Z80::OR8ao: Opc = Z80::OR8ap; break; + case Z80::CP8ao: Opc = Z80::CP8ap; break; + case Z80::TST8ao: Opc = Z80::TST8ap; break; + case Z80::LEA24ro: + case Z80::LEA16ro: + Opc = TargetOpcode::COPY; + break; + } + if (Opc == TargetOpcode::COPY) { + TII.copyPhysReg(MBB, ++II, DL, MI.getOperand(0).getReg(), + MI.getOperand(1).getReg()); + MI.eraseFromParent(); + } else { + MI.setDesc(TII.get(Opc)); + MI.RemoveOperand(FIOperandNum + 1); + } + } + } else { + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(BasePtr); + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::LD24ri : Z80::LD16ri), OffReg) + .addImm(Off); + if (SaveFlags) + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)); + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::ADD24ao : Z80::ADD16ao), + BasePtr).addReg(BasePtr).addReg(OffReg, RegState::Kill) + ->addRegisterDead(Z80::F, this); + if (SaveFlags) + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::POP24AF : Z80::POP16AF)); + if (Opc == Z80::PEA24o || Opc == Z80::PEA16o) { + MI.setDesc(TII.get(Opc == Z80::PEA24o ? Z80::EX24SP : Z80::EX16SP)); + MI.getOperand(0).ChangeToRegister(BasePtr, true); + MI.getOperand(1).ChangeToRegister(BasePtr, false); + MI.tieOperands(0, 1); + } else { + MI.getOperand(FIOperandNum).ChangeToRegister(BasePtr, false); + MI.getOperand(FIOperandNum + 1).ChangeToImmediate(0); + BuildMI(MBB, ++II, DL, TII.get(Is24Bit ? Z80::POP24r : Z80::POP16r), + BasePtr); + } + } +} + +Register Z80RegisterInfo::getFrameRegister(const MachineFunction &MF) const { + return getFrameLowering(MF)->hasFP(MF) ? (Is24Bit ? Z80::UIX : Z80::IX) + : (Is24Bit ? Z80::SPL : Z80::SPS); +} + +bool Z80RegisterInfo:: +shouldCoalesce(MachineInstr *MI, + const TargetRegisterClass *SrcRC, unsigned SrcSubReg, + const TargetRegisterClass *DstRC, unsigned DstSubReg, + const TargetRegisterClass *NewRC, LiveIntervals &LIS) const { + LLVM_DEBUG( + dbgs() << getRegClassName(SrcRC) << '[' << SrcRC->getNumRegs() + << "]:" << (SrcSubReg ? getSubRegIndexName(SrcSubReg) : "") + << " -> " << getRegClassName(DstRC) << '[' << DstRC->getNumRegs() + << "]:" << (DstSubReg ? getSubRegIndexName(DstSubReg) : "") << ' ' + << getRegClassName(NewRC) << '[' << NewRC->getNumRegs() << "]\n"); + // Don't coalesce if SrcRC and DstRC have a small intersection. + return std::min(SrcRC->getNumRegs(), DstRC->getNumRegs()) <= + NewRC->getNumRegs(); +} + +bool Z80RegisterInfo::requiresVirtualBaseRegisters( + const MachineFunction &MF) const { + return true; +} +int64_t Z80RegisterInfo::getFrameIndexInstrOffset(const MachineInstr *MI, + int Idx) const { + return MI->getOperand(Idx + 1).getImm(); +} +bool Z80RegisterInfo::needsFrameBaseReg(MachineInstr *MI, + int64_t Offset) const { + const MachineFunction &MF = *MI->getParent()->getParent(); + return !isFrameOffsetLegal(MI, getFrameRegister(MF), Offset); +} +void Z80RegisterInfo::materializeFrameBaseRegister(MachineBasicBlock *MBB, + Register BaseReg, + int FrameIdx, + int64_t Offset) const { + MachineFunction &MF = *MBB->getParent(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + const Z80Subtarget &STI = MF.getSubtarget(); + const Z80InstrInfo &TII = *STI.getInstrInfo(); + MachineBasicBlock::iterator II = MBB->begin(); + DebugLoc DL = MBB->findDebugLoc(II); + MRI.setRegClass(BaseReg, Is24Bit ? &Z80::I24RegClass : &Z80::I16RegClass); + BuildMI(*MBB, II, DL, TII.get(Is24Bit ? Z80::LEA24ro : Z80::LEA16ro), BaseReg) + .addFrameIndex(FrameIdx).addImm(Offset); + return; + Register CopyReg = MRI.createVirtualRegister(Is24Bit ? &Z80::I24RegClass + : &Z80::I16RegClass); + Register OffsetReg = MRI.createVirtualRegister(Is24Bit ? &Z80::O24RegClass + : &Z80::O16RegClass); + TII.copyPhysReg(*MBB, II, DL, CopyReg, getFrameRegister(MF)); + BuildMI(*MBB, II, DL, TII.get(Is24Bit ? Z80::LD24ri : Z80::LD16ri), OffsetReg) + .addImm(Offset); + BuildMI(*MBB, II, DL, TII.get(Is24Bit ? Z80::ADD24ao : Z80::ADD16ao), BaseReg) + .addReg(CopyReg).addReg(OffsetReg)->addRegisterDead(Z80::F, this); +} +void Z80RegisterInfo::resolveFrameIndex(MachineInstr &MI, Register BaseReg, + int64_t Offset) const { + unsigned FIOperandNum = 0; + while (!MI.getOperand(FIOperandNum).isFI()) { + FIOperandNum++; + assert(FIOperandNum < MI.getNumOperands() && "Expected a frame index"); + } + MI.getOperand(FIOperandNum).ChangeToRegister(BaseReg, false); + MI.getOperand(FIOperandNum + 1) + .ChangeToImmediate(MI.getOperand(FIOperandNum + 1).getImm() + Offset); +} +bool Z80RegisterInfo::isFrameOffsetLegal(const MachineInstr *MI, + Register BaseReg, + int64_t Offset) const { + unsigned Idx; + for (Idx = 0; !MI->getOperand(Idx).isFI(); ++Idx) + assert(Idx + 2 < MI->getNumOperands() && + "Instr doesn't have FrameIndex operand!"); + Offset += getFrameIndexInstrOffset(MI, Idx); + unsigned Opc = MI->getOpcode(); + return isInt<8>(Offset) && + ((Opc != Z80::LD88ro && Opc != Z80::LD88or) || isInt<8>(Offset + 1)); +} diff --git a/llvm/lib/Target/Z80/Z80RegisterInfo.h b/llvm/lib/Target/Z80/Z80RegisterInfo.h new file mode 100644 index 00000000000000..478e988dab154b --- /dev/null +++ b/llvm/lib/Target/Z80/Z80RegisterInfo.h @@ -0,0 +1,123 @@ +//===-- Z80RegisterInfo.h - Z80 Register Information Impl -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Z80 implementation of the TargetRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80REGISTERINFO_H +#define LLVM_LIB_TARGET_Z80_Z80REGISTERINFO_H + +#include "llvm/CodeGen/TargetRegisterInfo.h" + +#define GET_REGINFO_HEADER +#include "Z80GenRegisterInfo.inc" + +namespace llvm { +class Triple; + +class Z80RegisterInfo final : public Z80GenRegisterInfo { + /// Is24bit - Is the target 24-bits. + /// + bool Is24Bit; + + /// SlotSize - Stack slot size in bytes. + /// + unsigned SlotSize; + + /// StackPtr - Z80 physical register used as stack ptr. + /// + unsigned StackPtr; + +public: + Z80RegisterInfo(const Triple &TT); + + /// Code Generation virtual methods... + /// + bool trackLivenessAfterRegAlloc(const MachineFunction &MF) const override { + // Z80MachineLateOptimization requires liveness. + return true; + } + + /// getPointerRegClass - Returns a TargetRegisterClass used for pointer + /// values. + const TargetRegisterClass * + getPointerRegClass(const MachineFunction &MF, + unsigned Kind = 0) const override; + const TargetRegisterClass * + getLargestLegalSuperClass(const TargetRegisterClass *RC, + const MachineFunction &) const override; + + unsigned getRegPressureLimit(const TargetRegisterClass *RC, + MachineFunction &MF) const override; + + /// getCalleeSavedRegs - Return a null-terminated list of all of the + /// callee-save registers on this target. + const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override; + const uint32_t *getCallPreservedMask(const MachineFunction &MF, + CallingConv::ID CC) const override; + const uint32_t *getNoPreservedMask() const override; + + /// getReservedRegs - Returns a bitset indexed by physical register number + /// indicating if a register is a special register that has particular uses + /// and should be considered unavailable at all times, e.g. SP, RA. This is + /// used by register scaverger to determine what registers are free. + BitVector getReservedRegs(const MachineFunction &MF) const override; + + bool requiresRegisterScavenging(const MachineFunction &MF) const override { + return true; + } + bool requiresFrameIndexScavenging( + const MachineFunction &MF) const override { + return true; + } + bool requiresFrameIndexReplacementScavenging( + const MachineFunction &MF) const override { + return true; + } + + bool saveScavengerRegister(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + MachineBasicBlock::iterator &UseMI, + const TargetRegisterClass *RC, + Register Reg) const override; + + void eliminateFrameIndex(MachineBasicBlock::iterator MI, + int SPAdj, unsigned FIOperandNum, + RegScavenger *RS = nullptr) const override; + + // Support for virtual base registers. + bool requiresVirtualBaseRegisters(const MachineFunction &MF) const override; + int64_t getFrameIndexInstrOffset(const MachineInstr *MI, + int Idx) const override; + bool needsFrameBaseReg(MachineInstr *MI, int64_t Offset) const override; + void materializeFrameBaseRegister(MachineBasicBlock *MBB, Register BaseReg, + int FrameIdx, + int64_t Offset) const override; + void resolveFrameIndex(MachineInstr &MI, Register BaseReg, + int64_t Offset) const override; + bool isFrameOffsetLegal(const MachineInstr *MI, Register BaseReg, + int64_t Offset) const override; + + // Debug information queries. + Register getFrameRegister(const MachineFunction &MF) const override; + Register getStackRegister() const { return StackPtr; } + + /// \brief SrcRC and DstRC will be morphed into NewRC if this returns true. + bool shouldCoalesce(MachineInstr *MI, + const TargetRegisterClass *SrcRC, + unsigned SubReg, + const TargetRegisterClass *DstRC, + unsigned DstSubReg, + const TargetRegisterClass *NewRC, + LiveIntervals &LIS) const override; +}; +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/Z80/Z80RegisterInfo.td b/llvm/lib/Target/Z80/Z80RegisterInfo.td new file mode 100644 index 00000000000000..5f5f2716044c30 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80RegisterInfo.td @@ -0,0 +1,127 @@ +//===- Z80RegisterInfo.td - Describe the Z80 Register File --*- tablegen -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the Z80 Register file, defining the registers themselves, +// aliases between the registers, and the register classes built out of the +// registers. +// +//===----------------------------------------------------------------------===// + +class Z80Reg Enc = -1> : Register { + let Namespace = "Z80"; + let HWEncoding = Enc; +} +class Z80RegWithSubRegs sub = [], bits<16> enc = -1> + : Z80Reg { + let SubRegs = sub; +} + +// Subregister indices. +let Namespace = "Z80" in { + def sub_low : SubRegIndex<8>; + def sub_high : SubRegIndex<8, 8>; + def sub_short : SubRegIndex<16>; +} + +//===----------------------------------------------------------------------===// +// Register definitions... +// + +// 8-bit registers +def A : Z80Reg<"a", 7>; +def F : Z80Reg<"f">; +def B : Z80Reg<"b", 0>; +def C : Z80Reg<"c", 1>; +def D : Z80Reg<"d", 2>; +def E : Z80Reg<"e", 3>; +def H : Z80Reg<"h", 4>; +def L : Z80Reg<"l", 5>; + +// 8-bit index registers +let CostPerUse = 1 in { +def IXH : Z80Reg<"ixh", 4>; +def IXL : Z80Reg<"ixl", 5>; +def IYH : Z80Reg<"iyh", 4>; +def IYL : Z80Reg<"iyl", 5>; +} + +let SubRegIndices = [sub_high, sub_low], CoveredBySubRegs = 1 in { +// 16-bit registers +def AF : Z80RegWithSubRegs<"af", [A,F], 3>; +def BC : Z80RegWithSubRegs<"bc", [B,C], 0>; +def DE : Z80RegWithSubRegs<"de", [D,E], 1>; +def HL : Z80RegWithSubRegs<"hl", [H,L], 2>; +// 16-bit index registers +let CostPerUse = 1 in { +def IX : Z80RegWithSubRegs<"ix", [IXH,IXL], 2>; +def IY : Z80RegWithSubRegs<"iy", [IYH,IYL], 2>; +} +} +def SPS : Z80Reg<"sp", 3>; + +let SubRegIndices = [sub_short] in { +// 24-bit registers +def UBC : Z80RegWithSubRegs<"bc", [BC], 0>; +def UDE : Z80RegWithSubRegs<"de", [DE], 1>; +def UHL : Z80RegWithSubRegs<"hl", [HL], 2>; +// 24-bit index registers +let CostPerUse = 1 in { +def UIX : Z80RegWithSubRegs<"ix", [IX], 2>; +def UIY : Z80RegWithSubRegs<"iy", [IY], 2>; +} +} +def SPL : Z80Reg<"sp", 3>; +def PC : Z80Reg<"pc">; + +//===----------------------------------------------------------------------===// +// Register Class Definitions... +// + +class Z80RC8 : RegisterClass<"Z80", [i8 ], 8, regList>; +class Z80RC16 : RegisterClass<"Z80", [i16], 8, regList>; +class Z80RC24 : RegisterClass<"Z80", [i24], 8, regList>; + +def G8 : Z80RC8 <(add A, L, E, C, H, D, B)>; +def O8 : Z80RC8 <(add A, E, C, D, B)>; +def Y8 : Z80RC8 <(add O8, IYL, IYH)>; +def X8 : Z80RC8 <(add O8, IXL, IXH)>; +def I8 : Z80RC8 <(add IYL, IYH, IXL, IXH)>; +def R8 : Z80RC8 <(add G8, I8)>; +let CopyCost = -1 in +def F8 : Z80RC8 <(add F)>; + +def O16 : Z80RC16<(add DE, BC)>; +def G16 : Z80RC16<(add HL, O16)>; +def Y16 : Z80RC16<(add IY, O16)>; +def X16 : Z80RC16<(add IX, O16)>; +def I16 : Z80RC16<(add IY, IX)>; +def A16 : Z80RC16<(add HL, I16)>; +def R16 : Z80RC16<(add G16, I16)>; +let CopyCost = -1 in +def Z16 : Z80RC16<(add SPS, AF)>; +//def S16 : Z80RC16<(add R16, AF)>; +//def L16 : Z80RC16<(add G16, I16)>; +//def R16 : Z80RC16<(add L16, SPS)>; +//def S16 : Z80RC16<(add L16, AF)>; +//def C16 : Z80RC16<(add R16, SPS)>; + +def O24 : Z80RC24<(add UDE, UBC)>; +def G24 : Z80RC24<(add UHL, O24)>; +def Y24 : Z80RC24<(add UIY, O24)>; +def X24 : Z80RC24<(add UIX, O24)>; +def I24 : Z80RC24<(add UIY, UIX)>; +def A24 : Z80RC24<(add UHL, I24)>; +def R24 : Z80RC24<(add G24, I24)>; +let CopyCost = -1 in +def Z24 : Z80RC24<(add SPL)>; +//def S24 : Z80RC24<(add R24, AF)>; +//def L24 : Z80RC24<(add G24, I24)>; +//def R24 : Z80RC24<(add L24, SPL)>; +//def S24 : Z80RC24<(add L24, AF)>; +//def C24 : Z80RC24<(add R24, SPL)>; diff --git a/llvm/lib/Target/Z80/Z80Subtarget.cpp b/llvm/lib/Target/Z80/Z80Subtarget.cpp new file mode 100644 index 00000000000000..62420fb7505f71 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80Subtarget.cpp @@ -0,0 +1,41 @@ +//===-- Z80Subtarget.cpp - Z80 Subtarget Information ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Z80 specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#include "Z80Subtarget.h" +#include "Z80.h" +#include "Z80TargetMachine.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +using namespace llvm; + +#define DEBUG_TYPE "z80-subtarget" + +#define GET_SUBTARGETINFO_TARGET_DESC +#define GET_SUBTARGETINFO_CTOR +#include "Z80GenSubtargetInfo.inc" + +Z80Subtarget &Z80Subtarget::initializeSubtargetDependencies(StringRef CPU, + StringRef FS) { + if (CPU.empty()) + CPU = TargetTriple.getArchName(); + ParseSubtargetFeatures(CPU, FS); + HasIdxHalfRegs = HasUndocOps || HasEZ80Ops; + return *this; +} + +Z80Subtarget::Z80Subtarget(const Triple &TT, StringRef CPU, StringRef FS, + const Z80TargetMachine &TM) + : Z80GenSubtargetInfo(TT, CPU, FS), TargetTriple(TT), + In16BitMode(TT.isArch16Bit() || TT.getEnvironment() == Triple::CODE16), + In24BitMode(!In16BitMode), + InstrInfo(initializeSubtargetDependencies(CPU, FS)), TLInfo(TM, *this), + FrameLowering(*this) {} diff --git a/llvm/lib/Target/Z80/Z80Subtarget.h b/llvm/lib/Target/Z80/Z80Subtarget.h new file mode 100644 index 00000000000000..18b878fad7ac8a --- /dev/null +++ b/llvm/lib/Target/Z80/Z80Subtarget.h @@ -0,0 +1,95 @@ +//===-- Z80Subtarget.h - Define Subtarget for the Z80 ----------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the Z80 specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80SUBTARGET_H +#define LLVM_LIB_TARGET_Z80_Z80SUBTARGET_H + +#include "Z80FrameLowering.h" +#include "Z80InstrInfo.h" +#include "Z80ISelLowering.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" + +#define GET_SUBTARGETINFO_HEADER +#include "Z80GenSubtargetInfo.inc" + +namespace llvm { +class Z80TargetMachine; + +class Z80Subtarget final : public Z80GenSubtargetInfo { + /// What processor and OS we're targeting. + Triple TargetTriple; + + /// True if compiling for 16-bit, false for 24-bit. + bool In16BitMode; + + /// True if compiling for 24-bit, false for 16-bit. + bool In24BitMode; + + /// True if target has undocumented z80 instructions. + bool HasUndocOps = false; + + /// True if target has z180 instructions. + bool HasZ180Ops = false; + + /// True if target has ez80 instructions. + bool HasEZ80Ops = false; + + /// True if target has index half registers (HasUndocOps || HasEZ80Ops). + bool HasIdxHalfRegs = false; + + // Ordering here is important. Z80InstrInfo initializes Z80RegisterInfo which + // Z80TargetLowering needs. + Z80InstrInfo InstrInfo; + Z80TargetLowering TLInfo; + Z80FrameLowering FrameLowering; + +public: + /// This constructor initializes the data members to match that + /// of the specified triple. + Z80Subtarget(const Triple &TT, StringRef CPU, StringRef FS, + const Z80TargetMachine &TM); + + const Z80TargetLowering *getTargetLowering() const override { + return &TLInfo; + } + const Z80InstrInfo *getInstrInfo() const override { return &InstrInfo; } + const Z80FrameLowering *getFrameLowering() const override { + return &FrameLowering; + } + const Z80RegisterInfo *getRegisterInfo() const override { + return &getInstrInfo()->getRegisterInfo(); + } + + /// ParseSubtargetFeatures - Parses features string setting specified + /// subtarget options. Definition of function is auto generated by tblgen. + void ParseSubtargetFeatures(StringRef CPU, StringRef FS); + +private: + Z80Subtarget &initializeSubtargetDependencies(StringRef CPU, StringRef FS); + void initializeEnvironment(); + +public: + const Triple &getTargetTriple() const { return TargetTriple; } + /// Is this ez80 (disregarding specific ABI / programming model) + bool is24Bit() const { return In24BitMode; } + bool is16Bit() const { return In16BitMode; } + bool hasUndocOps() const { return HasUndocOps; } + bool hasZ180Ops() const { return HasZ180Ops; } + bool hasEZ80Ops() const { return HasEZ80Ops; } + bool hasIndexHalfRegs() const { return HasIdxHalfRegs; } + bool has24BitEZ80Ops() const { return is24Bit() && hasEZ80Ops(); } + bool has16BitEZ80Ops() const { return is16Bit() && hasEZ80Ops(); } +}; +} // namespace llvm + +#endif diff --git a/llvm/lib/Target/Z80/Z80TargetMachine.cpp b/llvm/lib/Target/Z80/Z80TargetMachine.cpp new file mode 100644 index 00000000000000..47cccd11e4656f --- /dev/null +++ b/llvm/lib/Target/Z80/Z80TargetMachine.cpp @@ -0,0 +1,169 @@ +//===-- Z80TargetMachine.cpp - Define TargetMachine for the Z80 -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the Z80 specific subclass of TargetMachine. +// +//===----------------------------------------------------------------------===// + +#include "Z80TargetMachine.h" +#include "TargetInfo/Z80TargetInfo.h" +#include "Z80.h" +#include "Z80Subtarget.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/CodeGen/GlobalISel/CallLowering.h" +#include "llvm/CodeGen/GlobalISel/IRTranslator.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelect.h" +#include "llvm/CodeGen/GlobalISel/Legalizer.h" +#include "llvm/CodeGen/GlobalISel/RegBankSelect.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/Function.h" +#include "llvm/InitializePasses.h" +#include "llvm/Support/TargetRegistry.h" +using namespace llvm; + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeZ80Target() { + // Register the target. + RegisterTargetMachine X(getTheZ80Target()); + RegisterTargetMachine Y(getTheEZ80Target()); + + PassRegistry &PR = *PassRegistry::getPassRegistry(); + initializeGlobalISel(PR); +} + +static std::string computeDataLayout(const Triple &TT) { + bool IsEZ80 = TT.getArch() == Triple::ez80; + bool Is16Bit = TT.isArch16Bit() || TT.getEnvironment() == Triple::CODE16; + // Z80 is little endian and mangling is closest to MachO. + std::string Ret = "e-m:o"; + // Memory Address Width + Ret += Is16Bit ? "-p:16:8" : "-p:24:8"; + // Port Address Width + Ret += IsEZ80 ? "-p1:16:8" : "-p1:8:8"; + // Other Address Width + if (IsEZ80) + Ret += Is16Bit ? "-p2:24:8" : "-p2:16:8"; + Ret += "-i16:8-i24:8-i32:8-i48:8-i64:8-i96:8-f32:8-f64:8-a:8-n8:16"; + if (!Is16Bit) + Ret += ":24"; + Ret += "-S8"; + return Ret; +} + +static Reloc::Model getEffectiveRelocModel(Optional RM) { + if (RM) + return *RM; + return Reloc::Static; +} + +/// Create a Z80 target. +/// +Z80TargetMachine::Z80TargetMachine(const Target &T, const Triple &TT, + StringRef CPU, StringRef FS, + const TargetOptions &Options, + Optional RM, + Optional CM, + CodeGenOpt::Level OL, bool JIT) + : LLVMTargetMachine(T, computeDataLayout(TT), TT, CPU, FS, Options, + getEffectiveRelocModel(RM), + getEffectiveCodeModel(CM, CodeModel::Small), OL), + TLOF(std::make_unique()) { + initAsmInfo(); + + setGlobalISel(true); + setGlobalISelAbort(GlobalISelAbortMode::Enable); +} + +Z80TargetMachine::~Z80TargetMachine() {} + +const Z80Subtarget * +Z80TargetMachine::getSubtargetImpl(const Function &F) const { + Attribute CPUAttr = F.getFnAttribute("target-cpu"); + Attribute FSAttr = F.getFnAttribute("target-features"); + + StringRef CPU = !CPUAttr.hasAttribute(Attribute::None) + ? CPUAttr.getValueAsString() + : (StringRef)TargetCPU; + StringRef FS = !FSAttr.hasAttribute(Attribute::None) + ? FSAttr.getValueAsString() + : (StringRef)TargetFS; + + SmallString<512> Key; + Key.reserve(CPU.size() + FS.size()); + Key += CPU; + Key += FS; + + auto &I = SubtargetMap[Key]; + if (!I) { + // This needs to be done before we create a new subtarget since any + // creation will depend on the TM and the code generation flags on the + // function that reside in TargetOptions. + resetTargetOptions(F); + I = std::make_unique(TargetTriple, CPU, FS, *this); + } + return I.get(); +} + +//===----------------------------------------------------------------------===// +// Pass Pipeline Configuration +//===----------------------------------------------------------------------===// + +namespace { +/// Z80 Code Generator Pass Configuration Options. +class Z80PassConfig : public TargetPassConfig { +public: + Z80PassConfig(Z80TargetMachine &TM, PassManagerBase &PM) + : TargetPassConfig(TM, PM) {} + + Z80TargetMachine &getZ80TargetMachine() const { + return getTM(); + } + + bool addIRTranslator() override; + bool addLegalizeMachineIR() override; + bool addRegBankSelect() override; + bool addGlobalInstructionSelect() override; + void addFastRegAlloc() override; +}; +} // end anonymous namespace + +TargetPassConfig *Z80TargetMachine::createPassConfig(PassManagerBase &PM) { + return new Z80PassConfig(*this, PM); +} + +bool Z80PassConfig::addIRTranslator() { + addPass(new IRTranslator); + return false; +} + +bool Z80PassConfig::addLegalizeMachineIR() { + addPass(new Legalizer); + return false; +} + +bool Z80PassConfig::addRegBankSelect() { + addPass(new RegBankSelect); + return false; +} + +bool Z80PassConfig::addGlobalInstructionSelect() { + addPass(new InstructionSelect); + return false; +} + +void Z80PassConfig::addFastRegAlloc() { + // FastRegAlloc can't handle the register pressure on the Z80 + if (usingDefaultRegAlloc()) + addOptimizedRegAlloc(); + else + TargetPassConfig::addFastRegAlloc(); +} diff --git a/llvm/lib/Target/Z80/Z80TargetMachine.h b/llvm/lib/Target/Z80/Z80TargetMachine.h new file mode 100644 index 00000000000000..3c0a734b384a80 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80TargetMachine.h @@ -0,0 +1,43 @@ +//===-- Z80TargetMachine.h - Define TargetMachine for the Z80 ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the Sparc specific subclass of TargetMachine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80TARGETMACHINE_H +#define LLVM_LIB_TARGET_Z80_Z80TARGETMACHINE_H + +#include "Z80Subtarget.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { + +class Z80TargetMachine : public LLVMTargetMachine { + std::unique_ptr TLOF; + mutable StringMap> SubtargetMap; + +public: + Z80TargetMachine(const Target &T, const Triple &TT, StringRef CPU, + StringRef FS, const TargetOptions &Options, + Optional RM, Optional CM, + CodeGenOpt::Level OL, bool JIT); + ~Z80TargetMachine() override; + const Z80Subtarget *getSubtargetImpl(const Function &F) const override; + + // Set up the pass pipeline. + TargetPassConfig *createPassConfig(PassManagerBase &PM) override; + TargetLoweringObjectFile *getObjFileLowering() const override { + return TLOF.get(); + } +}; + +} // end namespace llvm + +#endif From 92b4a06c8158682af81ce641d6b0aba367ee178c Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 20 Oct 2019 17:32:52 +0200 Subject: [PATCH 13/21] [Z80] Add Z80 GlobalISel implementation. --- .../llvm/CodeGen/GlobalISel/CallLowering.h | 23 +- .../llvm/CodeGen/GlobalISel/LegalizerHelper.h | 1 + .../llvm/CodeGen/GlobalISel/LegalizerInfo.h | 13 +- llvm/include/llvm/CodeGen/MachineOperand.h | 5 + llvm/include/llvm/IR/RuntimeLibcalls.def | 10 + llvm/lib/CodeGen/GlobalISel/CallLowering.cpp | 352 +++-- .../CodeGen/GlobalISel/LegalizerHelper.cpp | 136 +- llvm/lib/CodeGen/MachineOperand.cpp | 9 + llvm/lib/Target/ARM/ARMCallLowering.cpp | 4 +- llvm/lib/Target/X86/X86CallLowering.cpp | 2 +- llvm/lib/Target/Z80/CMakeLists.txt | 7 +- llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp | 520 ++++++++ llvm/lib/Target/Z80/GISel/Z80CallLowering.h | 45 + .../Z80/GISel/Z80InstructionSelector.cpp | 1180 +++++++++++++++++ .../lib/Target/Z80/GISel/Z80LegalizerInfo.cpp | 469 +++++++ llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.h | 56 + .../Target/Z80/GISel/Z80RegisterBankInfo.cpp | 169 +++ .../Target/Z80/GISel/Z80RegisterBankInfo.h | 79 ++ llvm/lib/Target/Z80/Z80.h | 14 +- llvm/lib/Target/Z80/Z80.td | 1 + .../lib/Target/Z80/Z80GenRegisterBankInfo.def | 72 + llvm/lib/Target/Z80/Z80ISelLowering.cpp | 1 + llvm/lib/Target/Z80/Z80RegisterBanks.td | 13 + llvm/lib/Target/Z80/Z80Subtarget.cpp | 15 +- llvm/lib/Target/Z80/Z80Subtarget.h | 24 + .../GlobalISel/LegalizerHelperTest.cpp | 48 +- 26 files changed, 3064 insertions(+), 204 deletions(-) create mode 100644 llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp create mode 100644 llvm/lib/Target/Z80/GISel/Z80CallLowering.h create mode 100644 llvm/lib/Target/Z80/GISel/Z80InstructionSelector.cpp create mode 100644 llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.cpp create mode 100644 llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.h create mode 100644 llvm/lib/Target/Z80/GISel/Z80RegisterBankInfo.cpp create mode 100644 llvm/lib/Target/Z80/GISel/Z80RegisterBankInfo.h create mode 100644 llvm/lib/Target/Z80/Z80GenRegisterBankInfo.def create mode 100644 llvm/lib/Target/Z80/Z80RegisterBanks.td diff --git a/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h b/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h index 678cf129e9e5d5..342537dcdc46f8 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h @@ -50,13 +50,14 @@ class CallLowering { // if the argument was an incoming arg. SmallVector OrigRegs; Type *Ty; + const Value *Val; SmallVector Flags; bool IsFixed; - ArgInfo(ArrayRef Regs, Type *Ty, + ArgInfo(ArrayRef Regs, Type *Ty, const Value *V, ArrayRef Flags = ArrayRef(), bool IsFixed = true) - : Regs(Regs.begin(), Regs.end()), Ty(Ty), + : Regs(Regs.begin(), Regs.end()), Ty(Ty), Val(V), Flags(Flags.begin(), Flags.end()), IsFixed(IsFixed) { if (!Regs.empty() && Flags.empty()) this->Flags.push_back(ISD::ArgFlagsTy()); @@ -65,8 +66,16 @@ class CallLowering { (Regs.empty() || Regs[0] == 0)) && "only void types should have no register"); } + ArgInfo(ArrayRef Regs, Type *Ty, + ArrayRef Flags = ArrayRef(), + bool IsFixed = true) + : ArgInfo(Regs, Ty, nullptr, Flags, IsFixed) {} + ArgInfo(ArrayRef Regs, const Value *V, + ArrayRef Flags = ArrayRef(), + bool IsFixed = true) + : ArgInfo(Regs, V->getType(), V, Flags, IsFixed) {} - ArgInfo() : Ty(nullptr), IsFixed(false) {} + ArgInfo() : Ty(nullptr), Val(nullptr), IsFixed(false) {} }; struct CallLoweringInfo { @@ -151,9 +160,8 @@ class CallLowering { } /// Handle custom values, which may be passed into one or more of \p VAs. - /// \return The number of \p VAs that have been assigned after the first - /// one, and which should therefore be skipped from further - /// processing. + /// \return The number of \p VAs that have been assigned, and which should + /// therefore be skipped from further processing. virtual unsigned assignCustomValue(const ArgInfo &Arg, ArrayRef VAs) { // This is not a pure virtual method because not all targets need to worry @@ -172,6 +180,9 @@ class CallLowering { return AssignFn(ValNo, ValVT, LocVT, LocInfo, Flags, State); } + virtual bool prepareArg(CCValAssign &VA) { return true; } + virtual bool finalize(CCState &State) { return true; } + MachineIRBuilder &MIRBuilder; MachineRegisterInfo &MRI; CCAssignFn *AssignFn; diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h index 175a68651f6d6f..e2694edae7df00 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h @@ -282,6 +282,7 @@ class LegalizerHelper { LLT Ty); LegalizeResult narrowScalarInsert(MachineInstr &MI, unsigned TypeIdx, LLT Ty); + LegalizeResult narrowScalarUnary(MachineInstr &MI, unsigned TypeIdx, LLT Ty); LegalizeResult narrowScalarBasic(MachineInstr &MI, unsigned TypeIdx, LLT Ty); LegalizeResult narrowScalarExtended(MachineInstr &MI, unsigned TypeIdx, LLT Ty); diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h index 53838488e350a4..de7372beb3bfea 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h @@ -1212,17 +1212,14 @@ class LegalizerInfo { const MachineRegisterInfo &MRI) const; /// Called for instructions with the Custom LegalizationAction. - virtual bool legalizeCustom(LegalizerHelper &Helper, - MachineInstr &MI) const { + virtual bool legalizeCustom(LegalizerHelper &Helper, MachineInstr &MI) const { llvm_unreachable("must implement this if custom action is used"); } + virtual LegalizerHelper::LegalizeResult - legalizeCustom(MachineInstr &MI, MachineRegisterInfo &MRI, - MachineIRBuilder &MIRBuilder, GISelChangeObserver &Observer, - LegalizerHelper &Helper) const { - return legalizeCustom(Helper, MI) - ? LegalizerHelper::Legalized - : LegalizerHelper::UnableToLegalize; + legalizeCustomMaybeLegal(LegalizerHelper &Helper, MachineInstr &MI) const { + return legalizeCustom(Helper, MI) ? LegalizerHelper::Legalized + : LegalizerHelper::UnableToLegalize; } /// \returns true if MI is either legal or has been legalized and false if not diff --git a/llvm/include/llvm/CodeGen/MachineOperand.h b/llvm/include/llvm/CodeGen/MachineOperand.h index 0f252137364cf1..a7e5233c19bf87 100644 --- a/llvm/include/llvm/CodeGen/MachineOperand.h +++ b/llvm/include/llvm/CodeGen/MachineOperand.h @@ -730,6 +730,11 @@ class MachineOperand { /// the setImm method should be used. void ChangeToImmediate(int64_t ImmVal); + /// ChangeToCImmediate - Replace this operand with a new CI immediate operand + /// of the specified value. If an operand is known to be an CI immediate + /// already, the setCImm method should be used. + void ChangeToCImmediate(const ConstantInt *CI); + /// ChangeToFPImmediate - Replace this operand with a new FP immediate operand /// of the specified value. If an operand is known to be an FP immediate /// already, the setFPImm method should be used. diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.def b/llvm/include/llvm/IR/RuntimeLibcalls.def index ec32e94708b782..afed93fe8fc632 100644 --- a/llvm/include/llvm/IR/RuntimeLibcalls.def +++ b/llvm/include/llvm/IR/RuntimeLibcalls.def @@ -89,8 +89,10 @@ HANDLE_LIBCALL(SCMP, nullptr) HANDLE_LIBCALL(ADD_I32, nullptr) HANDLE_LIBCALL(ADD_I32_I8, nullptr) HANDLE_LIBCALL(ADD_I64, nullptr) +HANDLE_LIBCALL(ADD_I128, nullptr) HANDLE_LIBCALL(SUB_I32, nullptr) HANDLE_LIBCALL(SUB_I64, nullptr) +HANDLE_LIBCALL(SUB_I128, nullptr) HANDLE_LIBCALL(MUL_I8, "__mulqi3") HANDLE_LIBCALL(MUL_I16, "__mulhi3") HANDLE_LIBCALL(MUL_I24, nullptr) @@ -149,11 +151,13 @@ HANDLE_LIBCALL(POPCNT_I16, nullptr) HANDLE_LIBCALL(POPCNT_I24, nullptr) HANDLE_LIBCALL(POPCNT_I32, "__popcountsi2") HANDLE_LIBCALL(POPCNT_I64, "__popcountdi2") +HANDLE_LIBCALL(POPCNT_I128, nullptr) HANDLE_LIBCALL(BITREV_I8, nullptr) HANDLE_LIBCALL(BITREV_I16, nullptr) HANDLE_LIBCALL(BITREV_I24, nullptr) HANDLE_LIBCALL(BITREV_I32, nullptr) HANDLE_LIBCALL(BITREV_I64, nullptr) +HANDLE_LIBCALL(BITREV_I128, nullptr) // Floating-point HANDLE_LIBCALL(ADD_F32, "__addsf3") @@ -348,8 +352,14 @@ HANDLE_LIBCALL(LLRINT_F64, "llrint") HANDLE_LIBCALL(LLRINT_F80, "llrintl") HANDLE_LIBCALL(LLRINT_F128, "llrintl") HANDLE_LIBCALL(LLRINT_PPCF128, "llrintl") +HANDLE_LIBCALL(ABS_F32, "fabsf") +HANDLE_LIBCALL(ABS_F64, "fabs") +HANDLE_LIBCALL(ABS_F80, "fabsl") +HANDLE_LIBCALL(ABS_F128, "fabsl") +HANDLE_LIBCALL(ABS_PPCF128, "fabsl") HANDLE_LIBCALL(NEG_F32, nullptr) HANDLE_LIBCALL(NEG_F64, nullptr) +HANDLE_LIBCALL(NEG_F128, nullptr) HANDLE_LIBCALL(CMP_F32, nullptr) HANDLE_LIBCALL(CMP_F64, nullptr) diff --git a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp index 2e9f50a451d7fc..efab9a10459ba1 100644 --- a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp +++ b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp @@ -44,7 +44,7 @@ bool CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, const CallBase &CB, unsigned i = 0; unsigned NumFixedArgs = CB.getFunctionType()->getNumParams(); for (auto &Arg : CB.args()) { - ArgInfo OrigArg{ArgRegs[i], Arg->getType(), ISD::ArgFlagsTy{}, + ArgInfo OrigArg{ArgRegs[i], Arg, ISD::ArgFlagsTy{}, i < NumFixedArgs}; setArgFlags(OrigArg, i + AttributeList::FirstArgIndex, DL, CB); Info.OrigArgs.push_back(OrigArg); @@ -59,7 +59,7 @@ bool CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, const CallBase &CB, else Info.Callee = MachineOperand::CreateReg(GetCalleeReg(), false); - Info.OrigRet = ArgInfo{ResRegs, CB.getType(), ISD::ArgFlagsTy{}}; + Info.OrigRet = ArgInfo{ResRegs, &CB}; if (!Info.OrigRet.Ty->isVoidTy()) setArgFlags(Info.OrigRet, AttributeList::ReturnIndex, DL, CB); @@ -195,22 +195,26 @@ bool CallLowering::handleAssignments(CCState &CCInfo, unsigned NumArgs = Args.size(); for (unsigned i = 0; i != NumArgs; ++i) { - MVT CurVT = MVT::getVT(Args[i].Ty); - if (Handler.assignArg(i, CurVT, CurVT, CCValAssign::Full, Args[i], + MVT CurMVT = MVT::getVT(Args[i].Ty); + if (Handler.assignArg(i, CurMVT, CurMVT, CCValAssign::Full, Args[i], Args[i].Flags[0], CCInfo)) { - if (!CurVT.isValid()) - return false; + EVT CurVT = EVT::getEVT(Args[i].Ty); MVT NewVT = TLI->getRegisterTypeForCallingConv( - F.getContext(), CCInfo.getCallingConv(), EVT(CurVT)); + F.getContext(), CCInfo.getCallingConv(), CurVT); // If we need to split the type over multiple regs, check it's a scenario // we currently support. unsigned NumParts = TLI->getNumRegistersForCallingConv( F.getContext(), CCInfo.getCallingConv(), CurVT); - if (NumParts > 1) { - // For now only handle exact splits. - if (NewVT.getSizeInBits() * NumParts != CurVT.getSizeInBits()) + assert(NumParts && "NumParts should not be 0"); + bool Exact = NewVT.getSizeInBits() * NumParts == CurVT.getSizeInBits(); + + if (NumParts == 1) { + // Try to use the register type if we couldn't assign the VT. + if (Handler.assignArg(i, NewVT, NewVT, CCValAssign::Full, Args[i], + Args[i].Flags[0], CCInfo)) return false; + continue; } // For incoming arguments (physregs to vregs), we could have values in @@ -221,82 +225,68 @@ bool CallLowering::handleAssignments(CCState &CCInfo, // If we have outgoing args, then we have the opposite case. We have a // vreg with an LLT which we want to assign to a physical location, and // we might have to record that the value has to be split later. - if (Handler.isIncomingArgumentHandler()) { - if (NumParts == 1) { - // Try to use the register type if we couldn't assign the VT. - if (Handler.assignArg(i, NewVT, NewVT, CCValAssign::Full, Args[i], - Args[i].Flags[0], CCInfo)) - return false; + // If we're handling an incoming arg which is split over multiple regs. + // E.g. passing an s128 on AArch64. + // Otherwise, this type is passed via multiple registers in the calling + // convention. We need to extract the individual parts. + ISD::ArgFlagsTy OrigFlags = Args[i].Flags[0]; + Args[i].OrigRegs.push_back(Args[i].Regs[0]); + // We're going to replace the regs and flags with the split ones. + Args[i].Regs.clear(); + Args[i].Flags.clear(); + LLT NewLLT = getLLTForMVT(NewVT); + // For each split register, create and assign a vreg that will store + // the incoming component of the larger value. These will later be + // merged to form the final vreg. + for (unsigned Part = 0; Part < NumParts; ++Part) { + MVT PartVT = NewVT; + LLT PartLLT = NewLLT; + ISD::ArgFlagsTy Flags = OrigFlags; + if (Part == 0) { + Flags.setSplit(); } else { - // We're handling an incoming arg which is split over multiple regs. - // E.g. passing an s128 on AArch64. - ISD::ArgFlagsTy OrigFlags = Args[i].Flags[0]; - Args[i].OrigRegs.push_back(Args[i].Regs[0]); - Args[i].Regs.clear(); - Args[i].Flags.clear(); - LLT NewLLT = getLLTForMVT(NewVT); - // For each split register, create and assign a vreg that will store - // the incoming component of the larger value. These will later be - // merged to form the final vreg. - for (unsigned Part = 0; Part < NumParts; ++Part) { - Register Reg = - MIRBuilder.getMRI()->createGenericVirtualRegister(NewLLT); - ISD::ArgFlagsTy Flags = OrigFlags; - if (Part == 0) { - Flags.setSplit(); - } else { - Flags.setOrigAlign(Align(1)); - if (Part == NumParts - 1) - Flags.setSplitEnd(); - } - Args[i].Regs.push_back(Reg); - Args[i].Flags.push_back(Flags); - if (Handler.assignArg(i + Part, NewVT, NewVT, CCValAssign::Full, - Args[i], Args[i].Flags[Part], CCInfo)) { - // Still couldn't assign this smaller part type for some reason. - return false; + Flags.setOrigAlign(Align(1)); + if (Part == NumParts - 1) { + Flags.setSplitEnd(); + if (!Exact) { + unsigned LeftoverSize = + CurVT.getSizeInBits() - NewVT.getSizeInBits() * Part; + EVT LeftoverVT = EVT::getIntegerVT(F.getContext(), LeftoverSize); + PartVT = TLI->getRegisterTypeForCallingConv( + F.getContext(), CCInfo.getCallingConv(), LeftoverVT); + PartLLT = getLLTForMVT(PartVT); } } } - } else { - // Handling an outgoing arg that might need to be split. - if (NumParts < 2) - return false; // Don't know how to deal with this type combination. - - // This type is passed via multiple registers in the calling convention. - // We need to extract the individual parts. - Register LargeReg = Args[i].Regs[0]; - LLT SmallTy = LLT::scalar(NewVT.getSizeInBits()); - auto Unmerge = MIRBuilder.buildUnmerge(SmallTy, LargeReg); - assert(Unmerge->getNumOperands() == NumParts + 1); - ISD::ArgFlagsTy OrigFlags = Args[i].Flags[0]; - // We're going to replace the regs and flags with the split ones. - Args[i].Regs.clear(); - Args[i].Flags.clear(); - for (unsigned PartIdx = 0; PartIdx < NumParts; ++PartIdx) { - ISD::ArgFlagsTy Flags = OrigFlags; - if (PartIdx == 0) { - Flags.setSplit(); - } else { - Flags.setOrigAlign(Align(1)); - if (PartIdx == NumParts - 1) - Flags.setSplitEnd(); - } - Args[i].Regs.push_back(Unmerge.getReg(PartIdx)); - Args[i].Flags.push_back(Flags); - if (Handler.assignArg(i + PartIdx, NewVT, NewVT, CCValAssign::Full, - Args[i], Args[i].Flags[PartIdx], CCInfo)) - return false; - } + Args[i].Regs.push_back( + MIRBuilder.getMRI()->createGenericVirtualRegister(PartLLT)); + Args[i].Flags.push_back(Flags); + if (Handler.assignArg(i + Part, PartVT, PartVT, CCValAssign::Full, + Args[i], Args[i].Flags[Part], CCInfo)) + // Still couldn't assign this smaller part type for some reason. + return false; } + if (Handler.isIncomingArgumentHandler()) + continue; + + if (Exact) + MIRBuilder.buildUnmerge(Args[i].Regs, Args[i].OrigRegs[0]); + else + for (unsigned Part = 0, Offset = 0; Part < NumParts; + ++Part, Offset += NewVT.getSizeInBits()) + MIRBuilder.buildExtract(Args[i].Regs[Part], Args[i].OrigRegs[0], + Offset); } } - for (unsigned i = 0, e = Args.size(), j = 0; i != e; ++i, ++j) { + int64_t IndirectOffset = 0; + for (unsigned i = 0, e = Args.size(), j = 0; i != e; ++i) { assert(j < ArgLocs.size() && "Skipped too many arg locs"); CCValAssign &VA = ArgLocs[j]; assert(VA.getValNo() == i && "Location doesn't correspond to current arg"); + if (!Handler.prepareArg(VA)) + return false; if (VA.needsCustom()) { unsigned NumArgRegs = @@ -308,18 +298,46 @@ bool CallLowering::handleAssignments(CCState &CCInfo, } // FIXME: Pack registers if we have more than one. - Register ArgReg = Args[i].Regs[0]; - - MVT OrigVT = MVT::getVT(Args[i].Ty); + LLT OrigTy = getLLTForType(*Args[i].Ty, DL); MVT VAVT = VA.getValVT(); - if (VA.isRegLoc()) { - if (Handler.isIncomingArgumentHandler() && VAVT != OrigVT) { - if (VAVT.getSizeInBits() < OrigVT.getSizeInBits()) { - // Expected to be multiple regs for a single incoming arg. - unsigned NumArgRegs = Args[i].Regs.size(); - if (NumArgRegs < 2) - return false; + if (VA.getLocInfo() == CCValAssign::Indirect) { + if (Args[i].Regs.size() > 1) { + LLVM_DEBUG(dbgs() << "Load/store a split arg to/from an indirect " + "pointer not implemented yet\n"); + return false; + } + assert(Args[i].OrigRegs.empty() && "Don't handle split yet"); + Args[i].OrigRegs.push_back(Args[i].Regs[0]); + Args[i].Regs.clear(); + LLT p0 = LLT::pointer(0, DL.getPointerSizeInBits(0)); + Register IndirectAddrReg; + IndirectOffset = 0; + if (Handler.isIncomingArgumentHandler()) + IndirectAddrReg = MIRBuilder.getMRI()->createGenericVirtualRegister(p0); + else { + MachineFrameInfo &MFI = MF.getFrameInfo(); + unsigned Size = VAVT.getStoreSize(); + unsigned Align = DL.getPrefTypeAlignment(Args[i].Ty); + int FI = MFI.CreateStackObject(Size, Align, false); + auto StackSlot = MIRBuilder.buildFrameIndex(p0, FI); + LLT sIndex = LLT::scalar(DL.getIndexSizeInBits(0)); + MIRBuilder.materializePtrAdd(IndirectAddrReg, StackSlot.getReg(0), + sIndex, IndirectOffset); + MachinePointerInfo MPO = MachinePointerInfo::getFixedStack(MF, FI); + CCValAssign IndirectVA = CCValAssign::getMem(i, VAVT, IndirectOffset, + VAVT, CCValAssign::Full); + Handler.assignValueToAddress(Args[i].OrigRegs[0], IndirectAddrReg, Size, + MPO, IndirectVA); + IndirectOffset += Size; + } + Args[i].Regs.push_back(IndirectAddrReg); + } + + unsigned NumArgRegs = Args[i].Regs.size(); + if (VA.isRegLoc()) { + if (Handler.isIncomingArgumentHandler()) { + if (VAVT.getSizeInBits() <= OrigTy.getSizeInBits()) { assert((j + (NumArgRegs - 1)) < ArgLocs.size() && "Too many regs for number of args"); for (unsigned Part = 0; Part < NumArgRegs; ++Part) { @@ -327,67 +345,112 @@ bool CallLowering::handleAssignments(CCState &CCInfo, VA = ArgLocs[j + Part]; Handler.assignValueToReg(Args[i].Regs[Part], VA.getLocReg(), VA); } - j += NumArgRegs - 1; - // Merge the split registers into the expected larger result vreg - // of the original call. - MIRBuilder.buildMerge(Args[i].OrigRegs[0], Args[i].Regs); - continue; - } - const LLT VATy(VAVT); - Register NewReg = - MIRBuilder.getMRI()->createGenericVirtualRegister(VATy); - Handler.assignValueToReg(NewReg, VA.getLocReg(), VA); - // If it's a vector type, we either need to truncate the elements - // or do an unmerge to get the lower block of elements. - if (VATy.isVector() && - VATy.getNumElements() > OrigVT.getVectorNumElements()) { - const LLT OrigTy(OrigVT); - // Just handle the case where the VA type is 2 * original type. - if (VATy.getNumElements() != OrigVT.getVectorNumElements() * 2) { - LLVM_DEBUG(dbgs() - << "Incoming promoted vector arg has too many elts"); - return false; - } - auto Unmerge = MIRBuilder.buildUnmerge({OrigTy, OrigTy}, {NewReg}); - MIRBuilder.buildCopy(ArgReg, Unmerge.getReg(0)); } else { - MIRBuilder.buildTrunc(ArgReg, {NewReg}).getReg(0); + const LLT VATy(VAVT); + Register NewReg = + MIRBuilder.getMRI()->createGenericVirtualRegister(VATy); + Handler.assignValueToReg(NewReg, VA.getLocReg(), VA); + // If it's a vector type, we either need to truncate the elements + // or do an unmerge to get the lower block of elements. + if (VATy.isVector() && + VATy.getNumElements() > OrigTy.getNumElements()) { + // Just handle the case where the VA type is 2 * original type. + if (VATy.getNumElements() != OrigTy.getNumElements() * 2) { + LLVM_DEBUG(dbgs() + << "Incoming promoted vector arg has too many elts"); + return false; + } + auto Unmerge = MIRBuilder.buildUnmerge({OrigTy, OrigTy}, {NewReg}); + MIRBuilder.buildCopy(Args[i].Regs[0], Unmerge.getReg(0)); + } else { + MIRBuilder.buildTrunc(Args[i].Regs[0], {NewReg}).getReg(0); + } } - } else if (!Handler.isIncomingArgumentHandler()) { - assert((j + (Args[i].Regs.size() - 1)) < ArgLocs.size() && + } else { + assert((j + (NumArgRegs - 1)) < ArgLocs.size() && "Too many regs for number of args"); // This is an outgoing argument that might have been split. - for (unsigned Part = 0; Part < Args[i].Regs.size(); ++Part) { + for (unsigned Part = 0; Part < NumArgRegs; ++Part) { // There should be Regs.size() ArgLocs per argument. VA = ArgLocs[j + Part]; Handler.assignValueToReg(Args[i].Regs[Part], VA.getLocReg(), VA); } - j += Args[i].Regs.size() - 1; - } else { - Handler.assignValueToReg(ArgReg, VA.getLocReg(), VA); - } - } else if (VA.isMemLoc()) { - // Don't currently support loading/storing a type that needs to be split - // to the stack. Should be easy, just not implemented yet. - if (Args[i].Regs.size() > 1) { - LLVM_DEBUG( - dbgs() - << "Load/store a split arg to/from the stack not implemented yet"); - return false; } - MVT VT = MVT::getVT(Args[i].Ty); - unsigned Size = VT == MVT::iPTR ? DL.getPointerSize() - : alignTo(VT.getSizeInBits(), 8) / 8; - unsigned Offset = VA.getLocMemOffset(); - MachinePointerInfo MPO; - Register StackAddr = Handler.getStackAddress(Size, Offset, MPO); - Handler.assignValueToAddress(Args[i], StackAddr, Size, MPO, VA); } else { - // FIXME: Support byvals and other weirdness - return false; + assert(VA.isMemLoc()); + assert((j + (NumArgRegs - 1)) < ArgLocs.size() && + "Too many regs for number of args"); + for (unsigned Part = 0; Part < NumArgRegs; ++Part) { + VA = ArgLocs[j + Part]; + unsigned Size; + ISD::ArgFlagsTy OrigFlags = Args[i].Flags[Part]; + if (OrigFlags.isByVal()) + Size = OrigFlags.getByValSize(); + else + Size = DL.getTypeStoreSize(Args[i].Ty); + if (!Size) + continue; + unsigned Offset = VA.getLocMemOffset(); + MachinePointerInfo MPO; + Register StackAddr = Handler.getStackAddress(Size, Offset, MPO); + if (!OrigFlags.isByVal()) + Handler.assignValueToAddress(Args[i].Regs[Part], StackAddr, Size, MPO, + VA); + else if (Handler.isIncomingArgumentHandler()) + MIRBuilder.buildCopy(Args[i].Regs[Part], StackAddr); + else { + Register SizeReg = + MIRBuilder + .buildConstant(LLT::scalar(DL.getIndexSizeInBits(0)), Size) + .getReg(0); + MaybeAlign ArgAlign; + const Value *ArgVal = Args[i].Val; + if (ArgVal) + ArgAlign = ArgVal->getPointerAlignment(DL); + MIRBuilder + .buildIntrinsic(Intrinsic::memcpy, ArrayRef(), + /*HasSideEffects=*/true) + .addUse(StackAddr) + .addUse(Args[i].Regs[0]) + .addUse(SizeReg) + .addImm(/*isTailCall=*/0) + .addMemOperand( + MF.getMachineMemOperand(MPO, MachineMemOperand::MOStore, Size, + OrigFlags.getNonZeroByValAlign())) + .addMemOperand(MF.getMachineMemOperand( + MachinePointerInfo(ArgVal), MachineMemOperand::MOLoad, Size, + ArgAlign.valueOrOne())); + } + } + } + if (Handler.isIncomingArgumentHandler() && NumArgRegs > 1) { + // Merge the split registers into the expected larger result vreg + // of the original call. + SmallVector Offsets; + for (unsigned Part = 0; Part < NumArgRegs; ++Part) + Offsets.push_back(Part * VAVT.getSizeInBits()); + MIRBuilder.buildSequence(Args[i].OrigRegs[0], Args[i].Regs, Offsets); } + + if (VA.getLocInfo() == CCValAssign::Indirect && + Handler.isIncomingArgumentHandler()) { + Register AddrReg; + unsigned Size = VAVT.getStoreSize(); + LLT sIndex = LLT::scalar(DL.getIndexSizeInBits(0)); + MIRBuilder.materializePtrAdd(AddrReg, Args[i].Regs[0], sIndex, + IndirectOffset); + MachinePointerInfo MPO(Args[i].Val, IndirectOffset); + CCValAssign IndirectVA = + CCValAssign::getMem(i, VAVT, IndirectOffset, VAVT, CCValAssign::Full); + Handler.assignValueToAddress(Args[i].OrigRegs[0], AddrReg, Size, MPO, + IndirectVA); + IndirectOffset += Size; + } + + j += Args[i].Regs.size(); } - return true; + + return Handler.finalize(CCInfo); } bool CallLowering::analyzeArgInfo(CCState &CCState, @@ -483,23 +546,18 @@ Register CallLowering::ValueHandler::extendRegister(Register ValReg, default: break; case CCValAssign::Full: case CCValAssign::BCvt: + case CCValAssign::Indirect: // FIXME: bitconverting between vector types may or may not be a // nop in big-endian situations. return ValReg; - case CCValAssign::AExt: { - auto MIB = MIRBuilder.buildAnyExt(LocTy, ValReg); - return MIB.getReg(0); - } - case CCValAssign::SExt: { - Register NewReg = MRI.createGenericVirtualRegister(LocTy); - MIRBuilder.buildSExt(NewReg, ValReg); - return NewReg; - } - case CCValAssign::ZExt: { - Register NewReg = MRI.createGenericVirtualRegister(LocTy); - MIRBuilder.buildZExt(NewReg, ValReg); - return NewReg; - } + case CCValAssign::AExt: + return MIRBuilder.buildAnyExt(LocTy, ValReg).getReg(0); + case CCValAssign::SExt: + return MIRBuilder.buildSExt(LocTy, ValReg).getReg(0); + case CCValAssign::ZExt: + return MIRBuilder.buildZExt(LocTy, ValReg).getReg(0); + case CCValAssign::Trunc: + return MIRBuilder.buildTrunc(LocTy, ValReg).getReg(0); } llvm_unreachable("unable to extend register"); } diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp index 5f48ec7c33185d..c4405cf6462978 100644 --- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp +++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp @@ -134,7 +134,7 @@ LegalizerHelper::legalizeInstrStep(MachineInstr &MI) { return moreElementsVector(MI, Step.TypeIdx, Step.NewType); case Custom: LLVM_DEBUG(dbgs() << ".. Custom legalization\n"); - return LI.legalizeCustom(MI, MRI, MIRBuilder, Observer, *this); + return LI.legalizeCustomMaybeLegal(*this, MI); default: LLVM_DEBUG(dbgs() << ".. Unable to legalize\n"); return UnableToLegalize; @@ -384,19 +384,63 @@ static RTLIB::Libcall getRTLibDesc(unsigned Opcode, unsigned Size) { } \ } while (0) - assert((Size == 32 || Size == 64 || Size == 128) && "Unsupported size"); +#define RTLIBCASEALL(LibcallPrefix) \ + do { \ + switch (Size) { \ + case 8: \ + return RTLIB::LibcallPrefix##8; \ + case 16: \ + return RTLIB::LibcallPrefix##16; \ + case 24: \ + return RTLIB::LibcallPrefix##24; \ + case 32: \ + return RTLIB::LibcallPrefix##32; \ + case 64: \ + return RTLIB::LibcallPrefix##64; \ + case 128: \ + return RTLIB::LibcallPrefix##128; \ + default: \ + llvm_unreachable("unexpected size"); \ + } \ + } while (0) switch (Opcode) { + case TargetOpcode::G_ADD: + RTLIBCASE(ADD_I); + case TargetOpcode::G_SUB: + RTLIBCASE(SUB_I); + case TargetOpcode::G_AND: + RTLIBCASEALL(AND_I); + case TargetOpcode::G_OR: + RTLIBCASEALL(OR_I); + case TargetOpcode::G_XOR: + RTLIBCASEALL(XOR_I); + case TargetOpcode::G_SHL: + RTLIBCASEALL(SHL_I); + case TargetOpcode::G_LSHR: + RTLIBCASEALL(SRL_I); + case TargetOpcode::G_ASHR: + RTLIBCASEALL(SRA_I); + case TargetOpcode::G_MUL: + RTLIBCASEALL(MUL_I); case TargetOpcode::G_SDIV: - RTLIBCASE(SDIV_I); + RTLIBCASEALL(SDIV_I); case TargetOpcode::G_UDIV: - RTLIBCASE(UDIV_I); + RTLIBCASEALL(UDIV_I); case TargetOpcode::G_SREM: - RTLIBCASE(SREM_I); + RTLIBCASEALL(SREM_I); case TargetOpcode::G_UREM: - RTLIBCASE(UREM_I); + RTLIBCASEALL(UREM_I); + case TargetOpcode::G_CTPOP: + RTLIBCASEALL(POPCNT_I); + case TargetOpcode::G_BITREVERSE: + RTLIBCASEALL(BITREV_I); case TargetOpcode::G_CTLZ_ZERO_UNDEF: RTLIBCASE(CTLZ_I); + case TargetOpcode::G_INTRINSIC_TRUNC: + RTLIBCASE(TRUNC_F); + case TargetOpcode::G_INTRINSIC_ROUND: + RTLIBCASE(ROUND_F); case TargetOpcode::G_FADD: RTLIBCASE(ADD_F); case TargetOpcode::G_FSUB: @@ -439,6 +483,10 @@ static RTLIB::Libcall getRTLibDesc(unsigned Opcode, unsigned Size) { RTLIBCASE(RINT_F); case TargetOpcode::G_FNEARBYINT: RTLIBCASE(NEARBYINT_F); + case TargetOpcode::G_FNEG: + RTLIBCASE(NEG_F); + case TargetOpcode::G_FABS: + RTLIBCASE(ABS_F); } llvm_unreachable("Unknown libcall function"); } @@ -623,14 +671,18 @@ LegalizerHelper::libcall(MachineInstr &MI) { switch (MI.getOpcode()) { default: return UnableToLegalize; + case TargetOpcode::G_ADD: + case TargetOpcode::G_SUB: case TargetOpcode::G_AND: case TargetOpcode::G_OR: case TargetOpcode::G_XOR: + case TargetOpcode::G_MUL: case TargetOpcode::G_SDIV: case TargetOpcode::G_UDIV: case TargetOpcode::G_SREM: case TargetOpcode::G_UREM: - case TargetOpcode::G_CTLZ_ZERO_UNDEF: { + case TargetOpcode::G_CTLZ_ZERO_UNDEF: + case TargetOpcode::G_BITREVERSE: { Type *HLTy = IntegerType::get(Ctx, Size); auto Status = simpleLibcall(MI, MIRBuilder, Size, HLTy); if (Status != Legalized) @@ -658,6 +710,8 @@ LegalizerHelper::libcall(MachineInstr &MI) { {{MI.getOperand(1).getReg(), OpTy}, {AmountReg, AmountTy}}); break; } + case TargetOpcode::G_INTRINSIC_TRUNC: + case TargetOpcode::G_INTRINSIC_ROUND: case TargetOpcode::G_FADD: case TargetOpcode::G_FSUB: case TargetOpcode::G_FMUL: @@ -678,7 +732,9 @@ LegalizerHelper::libcall(MachineInstr &MI) { case TargetOpcode::G_FMAXNUM: case TargetOpcode::G_FSQRT: case TargetOpcode::G_FRINT: - case TargetOpcode::G_FNEARBYINT: { + case TargetOpcode::G_FNEARBYINT: + case TargetOpcode::G_FNEG: + case TargetOpcode::G_FABS: { Type *HLTy = getFloatTypeForLLT(Ctx, LLTy); if (!HLTy || (Size != 32 && Size != 64 && Size != 128)) { LLVM_DEBUG(dbgs() << "No libcall available for size " << Size << ".\n"); @@ -845,14 +901,18 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, } case TargetOpcode::G_FREEZE: - return reduceOperationWidth(MI, TypeIdx, NarrowTy); + return narrowScalarUnary(MI, TypeIdx, NarrowTy); case TargetOpcode::G_ADD: case TargetOpcode::G_UADDO: case TargetOpcode::G_UADDE: + case TargetOpcode::G_SADDO: + case TargetOpcode::G_SADDE: case TargetOpcode::G_SUB: case TargetOpcode::G_USUBO: case TargetOpcode::G_USUBE: + case TargetOpcode::G_SSUBO: + case TargetOpcode::G_SSUBE: return narrowScalarExtended(MI, TypeIdx, NarrowTy); case TargetOpcode::G_MUL: case TargetOpcode::G_UMULH: @@ -3998,6 +4058,37 @@ LegalizerHelper::narrowScalarInsert(MachineInstr &MI, unsigned TypeIdx, return Legalized; } +LegalizerHelper::LegalizeResult +LegalizerHelper::narrowScalarUnary(MachineInstr &MI, unsigned TypeIdx, + LLT NarrowTy) { + Register DstReg = MI.getOperand(0).getReg(); + LLT DstTy = MRI.getType(DstReg); + + assert(MI.getNumOperands() == 2 && TypeIdx == 0); + + SmallVector DstRegs, SrcRegs; + LLT LeftoverTy; + Register DstLeftoverReg, SrcLeftoverReg; + if (!extractParts(MI.getOperand(1).getReg(), DstTy, NarrowTy, SrcRegs, + LeftoverTy, SrcLeftoverReg)) + return UnableToLegalize; + + for (unsigned I = 0, E = SrcRegs.size(); I != E; ++I) + DstRegs.push_back( + MIRBuilder.buildInstr(MI.getOpcode(), {NarrowTy}, {SrcRegs[I]}) + .getReg(0)); + + if (LeftoverTy.isValid()) + DstLeftoverReg = + MIRBuilder.buildInstr(MI.getOpcode(), {LeftoverTy}, {SrcLeftoverReg}) + .getReg(0); + + insertParts(DstReg, DstTy, NarrowTy, DstRegs, LeftoverTy, DstLeftoverReg); + + MI.eraseFromParent(); + return Legalized; +} + LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalarBasic(MachineInstr &MI, unsigned TypeIdx, LLT NarrowTy) { @@ -4037,25 +4128,42 @@ LegalizerHelper::narrowScalarExtended(MachineInstr &MI, unsigned TypeIdx, if (TypeIdx != 0) return UnableToLegalize; - unsigned Opc = MI.getOpcode(), OverflowOpc, ExtendOpc; + unsigned Opc = MI.getOpcode(), OverflowOpc, ExtendOpc, FinalExtendOpc; Register DstReg, ExtendOutReg, Src1Reg, Src2Reg, ExtendInReg; switch (Opc) { case TargetOpcode::G_ADD: case TargetOpcode::G_UADDO: case TargetOpcode::G_UADDE: + case TargetOpcode::G_SADDO: + case TargetOpcode::G_SADDE: OverflowOpc = TargetOpcode::G_UADDO; ExtendOpc = TargetOpcode::G_UADDE; break; case TargetOpcode::G_SUB: case TargetOpcode::G_USUBO: case TargetOpcode::G_USUBE: + case TargetOpcode::G_SSUBO: + case TargetOpcode::G_SSUBE: OverflowOpc = TargetOpcode::G_USUBO; ExtendOpc = TargetOpcode::G_USUBE; break; default: llvm_unreachable("Unexpected opcode"); } + switch (Opc) { + case TargetOpcode::G_SADDO: + case TargetOpcode::G_SADDE: + FinalExtendOpc = TargetOpcode::G_SADDE; + break; + case TargetOpcode::G_SSUBO: + case TargetOpcode::G_SSUBE: + FinalExtendOpc = TargetOpcode::G_SSUBE; + break; + default: + FinalExtendOpc = ExtendOpc; + break; + } { unsigned I = 0; @@ -4081,13 +4189,17 @@ LegalizerHelper::narrowScalarExtended(MachineInstr &MI, unsigned TypeIdx, unsigned NumParts = Src1Regs.size(); unsigned TotalParts = NumParts + LeftoverTy.isValid(); + assert(TotalParts > 1 && "Should be more that one part"); for (unsigned I = 0; I != TotalParts; ++I) { bool Leftover = I == NumParts; LLT PartTy = Leftover ? LeftoverTy : NarrowTy; Register PartDstReg = MRI.createGenericVirtualRegister(PartTy); Register PartExtendOutReg; - if (I == TotalParts - 1) + if (I == TotalParts - 1) { PartExtendOutReg = ExtendOutReg; + // Restore original signedness for most significant opcode. + ExtendOpc = FinalExtendOpc; + } if (!PartExtendOutReg.isValid()) PartExtendOutReg = MRI.createGenericVirtualRegister(LLT::scalar(1)); Register PartSrc1Reg = Leftover ? Src1LeftoverReg : Src1Regs[I]; @@ -4415,8 +4527,10 @@ LegalizerHelper::lowerBitCount(MachineInstr &MI, unsigned TypeIdx, LLT Ty) { MI.eraseFromParent(); return Legalized; } + Observer.changingInstr(MI); MI.setDesc(TII.get(TargetOpcode::G_CTPOP)); MI.getOperand(1).setReg(MIBTmp.getReg(0)); + Observer.changedInstr(MI); return Legalized; } case TargetOpcode::G_CTPOP: { diff --git a/llvm/lib/CodeGen/MachineOperand.cpp b/llvm/lib/CodeGen/MachineOperand.cpp index 2b4fd654e46c08..ea222d411a1eb9 100644 --- a/llvm/lib/CodeGen/MachineOperand.cpp +++ b/llvm/lib/CodeGen/MachineOperand.cpp @@ -162,6 +162,15 @@ void MachineOperand::ChangeToImmediate(int64_t ImmVal) { Contents.ImmVal = ImmVal; } +void MachineOperand::ChangeToCImmediate(const ConstantInt *CI) { + assert((!isReg() || !isTied()) && "Cannot change a tied operand into an imm"); + + removeRegFromUses(); + + OpKind = MO_CImmediate; + Contents.CI = CI; +} + void MachineOperand::ChangeToFPImmediate(const ConstantFP *FPImm) { assert((!isReg() || !isTied()) && "Cannot change a tied operand into an imm"); diff --git a/llvm/lib/Target/ARM/ARMCallLowering.cpp b/llvm/lib/Target/ARM/ARMCallLowering.cpp index 24ba9dfa44942b..8dea7af5fe5dd0 100644 --- a/llvm/lib/Target/ARM/ARMCallLowering.cpp +++ b/llvm/lib/Target/ARM/ARMCallLowering.cpp @@ -166,7 +166,7 @@ struct OutgoingValueHandler : public CallLowering::ValueHandler { assignValueToReg(NewRegs[0], VA.getLocReg(), VA); assignValueToReg(NewRegs[1], NextVA.getLocReg(), NextVA); - return 1; + return 2; } bool assignArg(unsigned ValNo, MVT ValVT, MVT LocVT, @@ -391,7 +391,7 @@ struct IncomingValueHandler : public CallLowering::ValueHandler { MIRBuilder.buildMerge(Arg.Regs[0], NewRegs); - return 1; + return 2; } /// Marking a physical register as used is different between formal diff --git a/llvm/lib/Target/X86/X86CallLowering.cpp b/llvm/lib/Target/X86/X86CallLowering.cpp index 4501b9ba34f621..00e0f5346090d3 100644 --- a/llvm/lib/Target/X86/X86CallLowering.cpp +++ b/llvm/lib/Target/X86/X86CallLowering.cpp @@ -72,7 +72,7 @@ bool X86CallLowering::splitToValueTypes(const ArgInfo &OrigArg, if (NumParts == 1) { // replace the original type ( pointer -> GPR ). SplitArgs.emplace_back(OrigArg.Regs[0], VT.getTypeForEVT(Context), - OrigArg.Flags, OrigArg.IsFixed); + OrigArg.Val, OrigArg.Flags, OrigArg.IsFixed); return true; } diff --git a/llvm/lib/Target/Z80/CMakeLists.txt b/llvm/lib/Target/Z80/CMakeLists.txt index 76a282b14807e9..debcbeb3c47a33 100644 --- a/llvm/lib/Target/Z80/CMakeLists.txt +++ b/llvm/lib/Target/Z80/CMakeLists.txt @@ -12,11 +12,16 @@ tablegen(LLVM Z80GenSubtargetInfo.inc -gen-subtarget) add_public_tablegen_target(Z80CommonTableGen) set(sources + GISel/Z80CallLowering.cpp + GISel/Z80InstructionSelector.cpp + GISel/Z80LegalizerInfo.cpp + GISel/Z80RegisterBankInfo.cpp Z80AsmPrinter.cpp Z80CallingConv.cpp Z80FrameLowering.cpp - Z80InstrInfo.cpp Z80ISelLowering.cpp + Z80InstrInfo.cpp + Z80MCInstLower.cpp Z80MCInstLower.cpp Z80RegisterInfo.cpp Z80Subtarget.cpp diff --git a/llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp b/llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp new file mode 100644 index 00000000000000..96e4215f48d685 --- /dev/null +++ b/llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp @@ -0,0 +1,520 @@ +//===- llvm/lib/Target/Z80/Z80CallLowering.cpp - Call lowering ------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// This file implements the lowering of LLVM calls to machine code calls for +/// GlobalISel. +// +//===----------------------------------------------------------------------===// + +#include "Z80CallLowering.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "Z80CallingConv.h" +#include "Z80ISelLowering.h" +#include "Z80MachineFunctionInfo.h" +#include "Z80Subtarget.h" +#include "llvm/CodeGen/Analysis.h" +#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" +#include "llvm/Support/Debug.h" +#include "llvm/Target/TargetMachine.h" +using namespace llvm; + +#define DEBUG_TYPE "z80-call-lowering" + +Z80CallLowering::Z80CallLowering(const Z80TargetLowering &TLI) + : CallLowering(&TLI) {} + +namespace { + +struct OutgoingValueHandler : public CallLowering::ValueHandler { + OutgoingValueHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + MachineInstrBuilder &MIB, CCAssignFn *AssignFn) + : ValueHandler(MIRBuilder, MRI, AssignFn), MIB(MIB), + DL(MIRBuilder.getMF().getDataLayout()), + STI(MIRBuilder.getMF().getSubtarget()) {} + + bool isIncomingArgumentHandler() const override { return false; } + + Register getStackAddress(uint64_t Size, int64_t Offset, + MachinePointerInfo &MPO) override { + LLT p0 = LLT::pointer(0, DL.getPointerSizeInBits(0)); + LLT SType = LLT::scalar(DL.getPointerSizeInBits(0)); + Register SPReg = MRI.createGenericVirtualRegister(p0); + MIRBuilder.buildCopy(SPReg, STI.getRegisterInfo()->getStackRegister()); + + Register OffsetReg = MRI.createGenericVirtualRegister(SType); + MIRBuilder.buildConstant(OffsetReg, Offset); + + Register AddrReg = MRI.createGenericVirtualRegister(p0); + MIRBuilder.buildPtrAdd(AddrReg, SPReg, OffsetReg); + + MPO = MachinePointerInfo::getStack(MIRBuilder.getMF(), Offset); + return AddrReg; + } + + void assignValueToReg(Register ValVReg, Register PhysReg, + CCValAssign &VA) override { + MIB.addUse(PhysReg, RegState::Implicit); + MIRBuilder.buildCopy(PhysReg, extendRegister(ValVReg, VA)); + } + + void assignValueToAddress(Register ValVReg, Register Addr, uint64_t Size, + MachinePointerInfo &MPO, CCValAssign &VA) override { + auto MMO = MIRBuilder.getMF().getMachineMemOperand( + MPO, MachineMemOperand::MOStore, VA.getLocVT().getStoreSize(), + Align()); + MIRBuilder.buildStore(extendRegister(ValVReg, VA), Addr, *MMO); + } + +protected: + MachineInstrBuilder &MIB; + const DataLayout &DL; + const Z80Subtarget &STI; +}; + +struct CallArgHandler : public OutgoingValueHandler { + CallArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + MachineInstrBuilder &MIB, CCAssignFn *AssignFn) + : OutgoingValueHandler(MIRBuilder, MRI, MIB, AssignFn), + After(MIRBuilder.getInsertPt()) {} + + bool prepareArg(CCValAssign &VA) override { + if (!VA.getValNo()) + Before = std::prev(MIRBuilder.getInsertPt()); + MIRBuilder.setInsertPt(MIRBuilder.getMBB(), std::next(Before)); + return OutgoingValueHandler::prepareArg(VA); + } + + void assignValueToReg(Register ValVReg, Register PhysReg, + CCValAssign &VA) override { + MIRBuilder.setInsertPt(MIRBuilder.getMBB(), After); + OutgoingValueHandler::assignValueToReg(ValVReg, PhysReg, VA); + } + + Register getStackAddress(uint64_t Size, int64_t Offset, + MachinePointerInfo &MPO) override { + return OutgoingValueHandler::getStackAddress(Size, Offset, MPO); + } + + void assignValueToAddress(Register ValVReg, Register Addr, uint64_t Size, + MachinePointerInfo &MPO, CCValAssign &VA) override { + if (MachineInstr *AddrMI = MRI.getVRegDef(Addr)) { + unsigned Size = STI.is24Bit() ? 3 : 2; + if (Size == VA.getLocVT().getStoreSize() && + AddrMI->getOpcode() == TargetOpcode::G_PTR_ADD) { + if (MachineInstr *BaseMI = + getDefIgnoringCopies(AddrMI->getOperand(1).getReg(), MRI)) { + if (auto OffConst = getConstantVRegValWithLookThrough( + AddrMI->getOperand(2).getReg(), MRI)) { + if (BaseMI->getOpcode() == TargetOpcode::COPY && + BaseMI->getOperand(1).getReg() == + STI.getRegisterInfo()->getStackRegister() && + OffConst->Value == SetupFrameAdjustment) { + MIRBuilder.setInsertPt(MIRBuilder.getMBB(), std::next(Before)); + MIRBuilder.buildInstr(Size == 3 ? Z80::PUSH24r : Z80::PUSH16r, {}, + {extendRegister(ValVReg, VA)}); + SetupFrameAdjustment += Size; + return; + } + } + } + } + } + LLT PtrTy = LLT::pointer(0, DL.getPointerSizeInBits(0)); + LLT OffTy = LLT::scalar(DL.getIndexSizeInBits(0)); + auto OffI = MIRBuilder.buildConstant(OffTy, -SetupFrameAdjustment); + Addr = MIRBuilder.buildPtrAdd(PtrTy, Addr, OffI).getReg(0); + return OutgoingValueHandler::assignValueToAddress(ValVReg, Addr, Size, MPO, + VA); + } + + bool finalize(CCState &State) override { + FrameSize = State.getNextStackOffset(); + MIRBuilder.setInsertPt(MIRBuilder.getMBB(), After); + return OutgoingValueHandler::finalize(State); + } + + unsigned getPreFrameAdjustment() const { + return 0; + } + + unsigned getFrameSize() const { + return FrameSize; + } + + unsigned getFrameTotalSize() const { + return getPreFrameAdjustment() + getFrameSize(); + } + + unsigned getSetupFrameAdjustment() const { + return SetupFrameAdjustment; + } + + unsigned getDestroyFrameAdjustment() const { + return 0; + } + +protected: + MachineBasicBlock::iterator After, Before; + unsigned FrameSize, SetupFrameAdjustment = 0; +}; + +struct IncomingValueHandler : public CallLowering::ValueHandler { + IncomingValueHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + CCAssignFn *AssignFn) + : ValueHandler(MIRBuilder, MRI, AssignFn), + DL(MIRBuilder.getMF().getDataLayout()) {} + + bool isIncomingArgumentHandler() const override { return true; } + + Register getStackAddress(uint64_t Size, int64_t Offset, + MachinePointerInfo &MPO) override { + auto &MFI = MIRBuilder.getMF().getFrameInfo(); + int FI = MFI.CreateFixedObject(Size, Offset, true); + MPO = MachinePointerInfo::getFixedStack(MIRBuilder.getMF(), FI); + LLT p0 = LLT::pointer(0, DL.getPointerSizeInBits(0)); + return MIRBuilder.buildFrameIndex(p0, FI).getReg(0); + } + + void assignValueToAddress(Register ValVReg, Register Addr, uint64_t Size, + MachinePointerInfo &MPO, CCValAssign &VA) override { + auto MMO = MIRBuilder.getMF().getMachineMemOperand( + MPO, MachineMemOperand::MOLoad | MachineMemOperand::MOInvariant, Size, + Align()); + MIRBuilder.buildLoad(ValVReg, Addr, *MMO); + } + + void assignValueToReg(Register ValVReg, Register PhysReg, + CCValAssign &VA) override { + markPhysRegUsed(PhysReg); + + switch (VA.getLocInfo()) { + default: { + // If we are copying the value from a physical register with the + // size larger than the size of the value itself - build the copy + // of the phys reg first and then build the truncation of that copy. + // The example of that would be copying from xmm0 to s32, for which + // case ValVT == LocVT == MVT::f32. If LocSize and ValSize are not equal + // we expect this to be handled in SExt/ZExt/AExt case. + unsigned PhysRegSize = + MRI.getTargetRegisterInfo()->getRegSizeInBits(PhysReg, MRI); + unsigned ValSize = VA.getValVT().getSizeInBits(); + unsigned LocSize = VA.getLocVT().getSizeInBits(); + if (PhysRegSize > ValSize && LocSize == ValSize) { + auto Copy = MIRBuilder.buildCopy(LLT::scalar(PhysRegSize), PhysReg); + MIRBuilder.buildTrunc(ValVReg, Copy); + return; + } + + MIRBuilder.buildCopy(ValVReg, PhysReg); + break; + } + case CCValAssign::LocInfo::SExt: + case CCValAssign::LocInfo::ZExt: + case CCValAssign::LocInfo::AExt: { + auto Copy = MIRBuilder.buildCopy(LLT{VA.getLocVT()}, PhysReg); + MIRBuilder.buildTrunc(ValVReg, Copy); + break; + } + } + } + + /// How the physical register gets marked varies between formal + /// parameters (it's a basic-block live-in), and a call instruction + /// (it's an implicit-def of the BL). + virtual void markPhysRegUsed(unsigned PhysReg) = 0; + +protected: + const DataLayout &DL; +}; + +struct FormalArgHandler : public IncomingValueHandler { + FormalArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + CCAssignFn *AssignFn) + : IncomingValueHandler(MIRBuilder, MRI, AssignFn) {} + + void markPhysRegUsed(unsigned PhysReg) override { + MIRBuilder.getMRI()->addLiveIn(PhysReg); + MIRBuilder.getMBB().addLiveIn(PhysReg); + } + + bool finalize(CCState &State) override { + MachineFunction &MF = MIRBuilder.getMF(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + if (State.isVarArg()) { + Z80MachineFunctionInfo &FuncInfo = *MF.getInfo(); + int FrameIdx = MFI.CreateFixedObject(1, State.getNextStackOffset(), true); + FuncInfo.setVarArgsFrameIndex(FrameIdx); + } + return true; + } +}; + +struct CallReturnHandler : public IncomingValueHandler { + CallReturnHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + CCAssignFn *AssignFn, MachineInstrBuilder &MIB) + : IncomingValueHandler(MIRBuilder, MRI, AssignFn), MIB(MIB) {} + + void markPhysRegUsed(unsigned PhysReg) override { + MIB.addDef(PhysReg, RegState::Implicit); + } + +protected: + MachineInstrBuilder &MIB; +}; + +} // end anonymous namespace + +void Z80CallLowering::splitToValueTypes(const ArgInfo &OrigArg, + SmallVectorImpl &SplitArgs, + const DataLayout &DL, + MachineRegisterInfo &MRI) const { + const Z80TargetLowering &TLI = *getTLI(); + LLVMContext &Ctx = OrigArg.Ty->getContext(); + + if (OrigArg.Ty->isVoidTy()) + return; + + SmallVector SplitVTs; + SmallVector Offsets; + ComputeValueVTs(TLI, DL, OrigArg.Ty, SplitVTs, &Offsets, 0); + + for (unsigned I = 0, E = SplitVTs.size(); I != E; ++I) { + Type *SplitTy = SplitVTs[I].getTypeForEVT(Ctx); + SplitArgs.emplace_back(OrigArg.Regs[I], SplitTy, OrigArg.Flags[0], + OrigArg.IsFixed); + } +} + +bool Z80CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, + CallLoweringInfo &Info) const { + MachineFunction &MF = MIRBuilder.getMF(); + const Function &F = MF.getFunction(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + const DataLayout &DL = F.getParent()->getDataLayout(); + const auto &STI = MF.getSubtarget(); + const Z80InstrInfo &TII = *STI.getInstrInfo(); + const Z80FrameLowering &TFI = *STI.getFrameLowering(); + const Z80RegisterInfo &TRI = *STI.getRegisterInfo(); + + // Look through bitcasts of the callee. + while (Info.Callee.isReg()) { + if (MachineInstr *MI = MRI.getVRegDef(Info.Callee.getReg())) { + switch (MI->getOpcode()) { + case TargetOpcode::COPY: + case TargetOpcode::G_GLOBAL_VALUE: + case TargetOpcode::G_INTTOPTR: + case TargetOpcode::G_CONSTANT: + Info.Callee = MI->getOperand(1); + continue; + } + } + break; + } + + SmallVector OutArgs; + for (const auto &OrigArg : Info.OrigArgs) { + if (OrigArg.Regs.size() > 1) + return false; + splitToValueTypes(OrigArg, OutArgs, DL, MRI); + } + + SmallVector InArgs; + if (!Info.OrigRet.Ty->isVoidTy()) { + if (Info.OrigRet.Regs.size() > 1) + return false; + splitToValueTypes(Info.OrigRet, InArgs, DL, MRI); + } + + auto CallSeqStart = MIRBuilder.buildInstr(TII.getCallFrameSetupOpcode()); + + // Create a temporarily-floating call instruction so we can add the implicit + // uses of arg registers. + bool Is24Bit = STI.is24Bit(); + unsigned CallOpc = Info.Callee.isReg() + ? Is24Bit ? Z80::CALL24r : Z80::CALL16r + : Is24Bit ? Z80::CALL24 : Z80::CALL16; + + auto MIB = MIRBuilder.buildInstrNoInsert(CallOpc) + .add(Info.Callee) + .addRegMask(TRI.getCallPreservedMask(MF, Info.CallConv)); + + // Do the actual argument marshalling. + CallArgHandler Handler(MIRBuilder, MRI, MIB, CC_Z80); + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, OutArgs, + Handler)) + return false; + + // Now we can add the actual call instruction to the correct basic block. + MIRBuilder.insertInstr(MIB); + + // If Callee is a reg, since it is used by a target specific + // instruction, it must have a register class matching the + // constraint of that instruction. + if (Info.Callee.isReg()) + MIB->getOperand(0).setReg(constrainOperandRegClass( + MF, TRI, MRI, TII, *MF.getSubtarget().getRegBankInfo(), *MIB, + MIB->getDesc(), Info.Callee, 0)); + + // Finally we can copy the returned value back into its virtual-register. In + // symmetry with the arguments, the physical register must be an + // implicit-define of the call instruction. + + if (!InArgs.empty()) { + SmallVector NewRegs; + + CallReturnHandler Handler(MIRBuilder, MRI, RetCC_Z80, MIB); + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, InArgs, + Handler)) + return false; + + if (!NewRegs.empty()) { + SmallVector Indices; + uint64_t Index = 0; + for (Register Reg : NewRegs) { + Indices.push_back(Index); + Index += MRI.getType(Reg).getSizeInBits(); + } + MIRBuilder.buildSequence(Info.OrigRet.Regs[0], NewRegs, Indices); + } + } + + CallSeqStart.addImm(Handler.getFrameSize()) + .addImm(Handler.getPreFrameAdjustment()) + .addImm(Handler.getSetupFrameAdjustment()); + + auto CallSeqEnd = MIRBuilder.buildInstr(TII.getCallFrameDestroyOpcode()) + .addImm(Handler.getFrameTotalSize()) + .addImm(Handler.getDestroyFrameAdjustment()); + + // It is too early to know exactly which method will be used, however + // sometimes a better method can be guaranteed and we can adjust the operands + // accordingly. + for (auto CallSeq : {CallSeqStart, CallSeqEnd}) { + const TargetRegisterClass *ScratchRC = nullptr; + switch (TFI.getOptimalStackAdjustmentMethod( + MF, TII.getFrameAdjustment(*CallSeq))) { + case Z80FrameLowering::SAM_None: + case Z80FrameLowering::SAM_Tiny: + case Z80FrameLowering::SAM_All: + // These methods do not need anything. + break; + case Z80FrameLowering::SAM_Small: + // This method clobbers an R register. + ScratchRC = Is24Bit ? &Z80::R24RegClass : &Z80::R16RegClass; + break; + case Z80FrameLowering::SAM_Large: + // This method also clobbers flags. + CallSeq.addDef(Z80::F, RegState::Implicit | RegState::Dead); + LLVM_FALLTHROUGH; + case Z80FrameLowering::SAM_Medium: + // These methods clobber an A register. + ScratchRC = Is24Bit ? &Z80::A24RegClass : &Z80::A16RegClass; + break; + } + if (ScratchRC) + CallSeq.addDef(MRI.createVirtualRegister(ScratchRC), + RegState::Implicit | RegState::Dead); + } + + return true; +} + +bool Z80CallLowering::lowerFormalArguments( + MachineIRBuilder &MIRBuilder, const Function &F, + ArrayRef> VRegs) const { + MachineFunction &MF = MIRBuilder.getMF(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + const DataLayout &DL = MF.getDataLayout(); + auto &FuncInfo = *MF.getInfo(); + + SmallVector SplitArgs; + unsigned Idx = 0; + for (auto &Arg : F.args()) { + if (!DL.getTypeStoreSize(Arg.getType())) + continue; + + // TODO: handle not simple cases. + if (Arg.hasAttribute(Attribute::InReg) || + Arg.hasAttribute(Attribute::SwiftSelf) || + Arg.hasAttribute(Attribute::SwiftError) || + Arg.hasAttribute(Attribute::Nest) || VRegs[Idx].size() > 1) + return false; + + if (Arg.hasAttribute(Attribute::StructRet)) + FuncInfo.setSRetReturnReg(VRegs[Idx][0]); + + ArgInfo OrigArg(VRegs[Idx], Arg.getType()); + setArgFlags(OrigArg, Idx + AttributeList::FirstArgIndex, DL, F); + splitToValueTypes(OrigArg, SplitArgs, DL, MRI); + Idx++; + } + + MachineBasicBlock &MBB = MIRBuilder.getMBB(); + if (!MBB.empty()) + MIRBuilder.setInstr(*MBB.begin()); + + FormalArgHandler Handler(MIRBuilder, MRI, CC_Z80); + if (!handleAssignments(F.getCallingConv(), F.isVarArg(), MIRBuilder, + SplitArgs, Handler)) + return false; + + // Move back to the end of the basic block. + MIRBuilder.setMBB(MBB); + + return true; +} + +bool Z80CallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, + const Value *Val, + ArrayRef VRegs) const { + assert(!Val == VRegs.empty() && "Return value without a vreg"); + MachineFunction &MF = MIRBuilder.getMF(); + LLVMContext &Ctx = MF.getFunction().getContext(); + auto &FuncInfo = *MF.getInfo(); + const auto &STI = MF.getSubtarget(); + auto MIB = + MIRBuilder.buildInstrNoInsert(STI.is24Bit() ? Z80::RET24 : Z80::RET16); + + Register SRetReturnReg = FuncInfo.getSRetReturnReg(); + assert((!SRetReturnReg || VRegs.empty()) && + "Struct ret should have void return"); + Type *RetTy = nullptr; + if (SRetReturnReg) { + VRegs = SRetReturnReg; + RetTy = Type::getInt8PtrTy(Ctx); + } else if (!VRegs.empty()) + RetTy = Val->getType(); + + if (!VRegs.empty()) { + const Function &F = MF.getFunction(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + const DataLayout &DL = MF.getDataLayout(); + const Z80TargetLowering &TLI = *getTLI(); + + SmallVector SplitEVTs; + ComputeValueVTs(TLI, DL, RetTy, SplitEVTs); + assert(VRegs.size() == SplitEVTs.size() && + "For each split Type there should be exactly one VReg."); + + SmallVector SplitArgs; + for (unsigned I = 0; I < SplitEVTs.size(); ++I) { + ArgInfo CurArgInfo = ArgInfo{VRegs[I], SplitEVTs[I].getTypeForEVT(Ctx)}; + setArgFlags(CurArgInfo, AttributeList::ReturnIndex, DL, F); + splitToValueTypes(CurArgInfo, SplitArgs, DL, MRI); + } + + OutgoingValueHandler Handler(MIRBuilder, MRI, MIB, RetCC_Z80); + if (!handleAssignments(F.getCallingConv(), F.isVarArg(), MIRBuilder, + SplitArgs, Handler)) + return false; + } + + MIRBuilder.insertInstr(MIB); + return true; +} diff --git a/llvm/lib/Target/Z80/GISel/Z80CallLowering.h b/llvm/lib/Target/Z80/GISel/Z80CallLowering.h new file mode 100644 index 00000000000000..b9ff1b59103925 --- /dev/null +++ b/llvm/lib/Target/Z80/GISel/Z80CallLowering.h @@ -0,0 +1,45 @@ +//===- llvm/lib/Target/Z80/Z80CallLowering.h - Call lowering ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// This file describes how to lower LLVM calls to machine code calls. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80CALLLOWERING_H +#define LLVM_LIB_TARGET_Z80_Z80CALLLOWERING_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/CodeGen/GlobalISel/CallLowering.h" + +namespace llvm { + +class Z80TargetLowering; + +class Z80CallLowering : public CallLowering { +public: + Z80CallLowering(const Z80TargetLowering &TLI); + + bool lowerCall(MachineIRBuilder &MIRBuilder, + CallLoweringInfo &Info) const override; + + bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F, + ArrayRef> VRegs) const override; + + bool lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val, + ArrayRef VRegs) const override; + +private: + void splitToValueTypes(const ArgInfo &OrigArg, + SmallVectorImpl &SplitArgs, + const DataLayout &DL, MachineRegisterInfo &MRI) const; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_Z80_Z80CALLLOWERING_H diff --git a/llvm/lib/Target/Z80/GISel/Z80InstructionSelector.cpp b/llvm/lib/Target/Z80/GISel/Z80InstructionSelector.cpp new file mode 100644 index 00000000000000..1e8dd7ab9b9645 --- /dev/null +++ b/llvm/lib/Target/Z80/GISel/Z80InstructionSelector.cpp @@ -0,0 +1,1180 @@ +//===- Z80InstructionSelector.cpp -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the targeting of the InstructionSelector class for +/// Z80. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "Z80.h" +#include "Z80MachineFunctionInfo.h" +#include "Z80RegisterBankInfo.h" +#include "Z80Subtarget.h" +#include "Z80TargetMachine.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelector.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/Support/Debug.h" +using namespace llvm; + +#define DEBUG_TYPE "Z80-isel" + +namespace { + +#define GET_GLOBALISEL_PREDICATE_BITSET +#include "Z80GenGlobalISel.inc" +#undef GET_GLOBALISEL_PREDICATE_BITSET + +class Z80InstructionSelector : public InstructionSelector { +public: + Z80InstructionSelector(const Z80TargetMachine &TM, const Z80Subtarget &STI, + const Z80RegisterBankInfo &RBI); + + bool select(MachineInstr &I) const; + bool select(MachineInstr &I) override { + return static_cast(this)->select(I); + } + static const char *getName() { return DEBUG_TYPE; } + +private: + /// tblgen-erated 'select' implementation, used as the initial selector for + /// the patterns that don't require complex C++. + bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const; + + bool selectConstant(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectTrunc(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectSExt(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectZExt(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectAnyExt(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectLoadStoreOp(MachineInstr &I, MachineRegisterInfo &MRI, + MachineFunction &MF) const; + bool selectFrameIndexOrGep(MachineInstr &I, MachineRegisterInfo &MRI, + MachineFunction &MF) const; + + bool selectCopy(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectUnmergeValues(MachineInstr &I, MachineRegisterInfo &MRI, + MachineFunction &MF) const; + bool selectMergeValues(MachineInstr &I, MachineRegisterInfo &MRI, + MachineFunction &MF) const; + + Z80::CondCode foldCompare(MachineInstr &I, MachineIRBuilder &MIB, + MachineRegisterInfo &MRI) const; + Z80::CondCode foldExtendedAddSub(MachineInstr &I, MachineIRBuilder &MIB, + MachineRegisterInfo &MRI) const; + Z80::CondCode foldSetCC(MachineInstr &I, MachineIRBuilder &MIB, + MachineRegisterInfo &MRI) const; + Z80::CondCode foldCond(Register CondReg, MachineIRBuilder &MIB, + MachineRegisterInfo &MRI) const; + bool selectSetCond(MachineInstr &I, MachineRegisterInfo &MRI) const; + + bool selectSelect(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectBrCond(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectBrJT(MachineInstr &I, MachineRegisterInfo &MRI, + MachineFunction &MF) const; + bool selectImplicitDefOrPHI(MachineInstr &I, MachineRegisterInfo &MRI) const; + + ComplexRendererFns selectMem(MachineOperand &Root) const; + ComplexRendererFns selectOff(MachineOperand &Root) const; + + const TargetRegisterClass *getRegClass(LLT Ty, const RegisterBank &RB) const; + const TargetRegisterClass *getRegClass(LLT Ty, unsigned Reg, + MachineRegisterInfo &MRI) const; + + const Z80TargetMachine &TM; + const Z80Subtarget &STI; + const Z80InstrInfo &TII; + const Z80RegisterInfo &TRI; + const Z80RegisterBankInfo &RBI; + +#define GET_GLOBALISEL_PREDICATES_DECL +#include "Z80GenGlobalISel.inc" +#undef GET_GLOBALISEL_PREDICATES_DECL + +#define GET_GLOBALISEL_TEMPORARIES_DECL +#include "Z80GenGlobalISel.inc" +#undef GET_GLOBALISEL_TEMPORARIES_DECL +}; + +} // end anonymous namespace + +#define GET_GLOBALISEL_IMPL +#include "Z80GenGlobalISel.inc" +#undef GET_GLOBALISEL_IMPL + +Z80InstructionSelector::Z80InstructionSelector(const Z80TargetMachine &TM, + const Z80Subtarget &STI, + const Z80RegisterBankInfo &RBI) + : InstructionSelector(), TM(TM), STI(STI), TII(*STI.getInstrInfo()), + TRI(*STI.getRegisterInfo()), RBI(RBI), +#define GET_GLOBALISEL_PREDICATES_INIT +#include "Z80GenGlobalISel.inc" +#undef GET_GLOBALISEL_PREDICATES_INIT +#define GET_GLOBALISEL_TEMPORARIES_INIT +#include "Z80GenGlobalISel.inc" +#undef GET_GLOBALISEL_TEMPORARIES_INIT +{ + (void)this->TM; +} + +// FIXME: This should be target-independent, inferred from the types declared +// for each class in the bank. +const TargetRegisterClass * +Z80InstructionSelector::getRegClass(LLT Ty, const RegisterBank &RB) const { + if (RB.getID() == Z80::GPRRegBankID) { + if (Ty.getSizeInBits() <= 8) + return &Z80::R8RegClass; + if (Ty.getSizeInBits() == 16) + return &Z80::R16RegClass; + if (Ty.getSizeInBits() == 24) + return &Z80::R24RegClass; + } + + llvm_unreachable("Unknown RegBank!"); +} + +const TargetRegisterClass * +Z80InstructionSelector::getRegClass(LLT Ty, unsigned Reg, + MachineRegisterInfo &MRI) const { + const RegisterBank &RegBank = *RBI.getRegBank(Reg, MRI, TRI); + return getRegClass(Ty, RegBank); +} + +static int64_t getSubRegIndex(const TargetRegisterClass *RC) { + unsigned SubIdx = Z80::NoSubRegister; + if (RC == &Z80::R16RegClass) + SubIdx = Z80::sub_short; + else if (RC == &Z80::R8RegClass) + SubIdx = Z80::sub_low; + return SubIdx; +} + +static const TargetRegisterClass *getRegClassFromGRPhysReg(Register Reg) { + assert(Reg.isPhysical()); + for (auto *RC : {&Z80::R8RegClass, &Z80::F8RegClass, &Z80::R16RegClass, + &Z80::Z16RegClass, &Z80::R24RegClass, &Z80::Z24RegClass}) + if (RC->contains(Reg)) + return RC; + llvm_unreachable("Unknown RegClass for PhysReg!"); +} + +// Set Z80 Opcode and constrain DstReg. +bool Z80InstructionSelector::selectCopy(MachineInstr &I, + MachineRegisterInfo &MRI) const { + Register DstReg = I.getOperand(0).getReg(); + const unsigned DstSize = RBI.getSizeInBits(DstReg, MRI, TRI); + const RegisterBank &DstRegBank = *RBI.getRegBank(DstReg, MRI, TRI); + + Register SrcReg = I.getOperand(1).getReg(); + const unsigned SrcSize = RBI.getSizeInBits(SrcReg, MRI, TRI); + const RegisterBank &SrcRegBank = *RBI.getRegBank(SrcReg, MRI, TRI); + + if (DstReg.isPhysical()) { + assert(I.isCopy() && "Generic operators do not allow physical registers"); + + if (DstReg == Z80::F && + !RBI.constrainGenericRegister(SrcReg, Z80::F8RegClass, MRI)) + return false; + + if (DstSize > SrcSize && SrcRegBank.getID() == Z80::GPRRegBankID && + DstRegBank.getID() == Z80::GPRRegBankID) { + + const TargetRegisterClass *SrcRC = + getRegClass(MRI.getType(SrcReg), SrcRegBank); + const TargetRegisterClass *DstRC = getRegClassFromGRPhysReg(DstReg); + + if (SrcRC != DstRC) + // This case can be generated by ABI lowering, perform anyext + I.getOperand(1).setReg( + MachineIRBuilder(I) + .buildInstr(TargetOpcode::SUBREG_TO_REG, {DstRC}, + {int64_t(0), SrcReg, getSubRegIndex(SrcRC)}) + .getReg(0)); + } + + return true; + } + + assert((!SrcReg.isPhysical() || I.isCopy()) && + "No phys reg on generic operators"); + assert((DstSize == SrcSize || + // Copies are a means to setup initial types, the number of + // bits may not exactly match. + (SrcReg.isPhysical() && + DstSize <= RBI.getSizeInBits(SrcReg, MRI, TRI))) && + "Copy with different width?!"); + + const TargetRegisterClass *DstRC = MRI.getRegClassOrNull(DstReg); + if (!DstRC) + DstRC = getRegClass(MRI.getType(DstReg), DstRegBank); + + if (SrcRegBank.getID() == Z80::GPRRegBankID && + DstRegBank.getID() == Z80::GPRRegBankID && SrcSize > DstSize && + SrcReg.isPhysical()) { + // Change the physical register to perform truncate. + + const TargetRegisterClass *SrcRC = getRegClassFromGRPhysReg(SrcReg); + + if (DstRC != SrcRC) { + I.getOperand(1).setSubReg(getSubRegIndex(DstRC)); + I.getOperand(1).substPhysReg(SrcReg, TRI); + } + } + + // No need to constrain SrcReg. It will get constrained when + // we hit another of its use or its defs. + // Copies do not have constraints. + const TargetRegisterClass *OldRC = MRI.getRegClassOrNull(DstReg); + if (!OldRC || !DstRC->hasSubClassEq(OldRC)) { + if (!RBI.constrainGenericRegister(DstReg, *DstRC, MRI)) { + LLVM_DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode()) + << " operand\n"); + return false; + } + } + I.setDesc(TII.get(Z80::COPY)); + return true; +} + +bool Z80InstructionSelector::select(MachineInstr &I) const { + assert(I.getParent() && "Instruction should be in a basic block!"); + assert(I.getParent()->getParent() && "Instruction should be in a function!"); + + MachineBasicBlock &MBB = *I.getParent(); + MachineFunction &MF = *MBB.getParent(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + + unsigned Opc = I.getOpcode(); + if (!isPreISelGenericOpcode(Opc)) { + switch (I.getOpcode()) { + case TargetOpcode::COPY: + return selectCopy(I, MRI); + case Z80::SetCC: + return selectSetCond(I, MRI); + } + return true; + } + + assert(I.getNumOperands() == I.getNumExplicitOperands() && + "Generic instruction has unexpected implicit operands"); + + if (selectImpl(I, *CoverageInfo)) + return true; + + LLVM_DEBUG(dbgs() << " C++ instruction selection: "; I.print(dbgs())); + + // TODO: This should be implemented by tblgen. + switch (I.getOpcode()) { + case TargetOpcode::G_CONSTANT: + case TargetOpcode::G_GLOBAL_VALUE: + case TargetOpcode::G_JUMP_TABLE: + return selectConstant(I, MRI); + case TargetOpcode::G_TRUNC: + return selectTrunc(I, MRI); + case TargetOpcode::G_PTRTOINT: + case TargetOpcode::G_INTTOPTR: + case TargetOpcode::G_FREEZE: + return selectCopy(I, MRI); + case TargetOpcode::G_SEXT: + return selectSExt(I, MRI); + case TargetOpcode::G_ZEXT: + return selectZExt(I, MRI); + case TargetOpcode::G_ANYEXT: + return selectAnyExt(I, MRI); + case TargetOpcode::G_LOAD: + case TargetOpcode::G_STORE: + return selectLoadStoreOp(I, MRI, MF); + case TargetOpcode::G_PTR_ADD: + case TargetOpcode::G_FRAME_INDEX: + return selectFrameIndexOrGep(I, MRI, MF); + case TargetOpcode::G_UNMERGE_VALUES: + return selectUnmergeValues(I, MRI, MF); + case TargetOpcode::G_MERGE_VALUES: + return selectMergeValues(I, MRI, MF); + case TargetOpcode::G_SELECT: + return selectSelect(I, MRI); + case TargetOpcode::G_UADDO: + case TargetOpcode::G_UADDE: + case TargetOpcode::G_USUBO: + case TargetOpcode::G_USUBE: + case TargetOpcode::G_SADDO: + case TargetOpcode::G_SADDE: + case TargetOpcode::G_SSUBO: + case TargetOpcode::G_SSUBE: + case TargetOpcode::G_ICMP: + return selectSetCond(I, MRI); + case TargetOpcode::G_BRCOND: + return selectBrCond(I, MRI); + case TargetOpcode::G_BRINDIRECT: + I.setDesc(TII.get(STI.is24Bit() ? Z80::JP24r : Z80::JP16r)); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); + case TargetOpcode::G_BRJT: + return selectBrJT(I, MRI, MF); + case TargetOpcode::G_IMPLICIT_DEF: + case TargetOpcode::G_PHI: + return selectImplicitDefOrPHI(I, MRI); + default: + return false; + } +} + +bool Z80InstructionSelector::selectConstant(MachineInstr &I, + MachineRegisterInfo &MRI) const { + assert((I.getOpcode() == TargetOpcode::G_CONSTANT || + I.getOpcode() == TargetOpcode::G_GLOBAL_VALUE || + I.getOpcode() == TargetOpcode::G_JUMP_TABLE) && + "unexpected instruction"); + + const Register DefReg = I.getOperand(0).getReg(); + LLT Ty = MRI.getType(DefReg); + + unsigned NewOpc; + switch (Ty.getSizeInBits()) { + case 8: + NewOpc = Z80::LD8ri; + break; + case 16: + NewOpc = Z80::LD16ri; + break; + case 24: + assert(STI.is24Bit() && "Illegal operand size."); + NewOpc = Z80::LD24ri; + break; + default: + llvm_unreachable("Unsupported type."); + } + + I.setDesc(TII.get(NewOpc)); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); +} + +bool Z80InstructionSelector::selectTrunc(MachineInstr &I, + MachineRegisterInfo &MRI) const { + assert((I.getOpcode() == TargetOpcode::G_TRUNC) && "unexpected instruction"); + + const Register DstReg = I.getOperand(0).getReg(); + const Register SrcReg = I.getOperand(1).getReg(); + + const LLT DstTy = MRI.getType(DstReg); + const LLT SrcTy = MRI.getType(SrcReg); + + const RegisterBank &DstRB = *RBI.getRegBank(DstReg, MRI, TRI); + const RegisterBank &SrcRB = *RBI.getRegBank(SrcReg, MRI, TRI); + + const TargetRegisterClass *DstRC = getRegClass(DstTy, DstRB); + const TargetRegisterClass *SrcRC = getRegClass(SrcTy, SrcRB); + + if (!DstRC || !SrcRC) + return false; + + unsigned SubIdx; + if (DstRC == SrcRC) + // Nothing to be done + SubIdx = Z80::NoSubRegister; + else if (DstRC == &Z80::R8RegClass) + SubIdx = Z80::sub_low; + else if (DstRC == &Z80::R16RegClass) + SubIdx = Z80::sub_short; + else + return false; + + SrcRC = TRI.getSubClassWithSubReg(SrcRC, SubIdx); + + if (!RBI.constrainGenericRegister(SrcReg, *SrcRC, MRI) || + !RBI.constrainGenericRegister(DstReg, *DstRC, MRI)) { + LLVM_DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode()) + << "\n"); + return false; + } + + I.getOperand(1).setSubReg(SubIdx); + + I.setDesc(TII.get(Z80::COPY)); + return true; +} + +bool Z80InstructionSelector::selectSExt(MachineInstr &I, + MachineRegisterInfo &MRI) const { + assert((I.getOpcode() == TargetOpcode::G_SEXT) && "unexpected instruction"); + + const Register DstReg = I.getOperand(0).getReg(); + const Register SrcReg = I.getOperand(1).getReg(); + + const LLT DstTy = MRI.getType(DstReg); + const LLT SrcTy = MRI.getType(SrcReg); + + if (SrcTy != LLT::scalar(1)) + return false; + + MachineIRBuilder MIB(I); + + unsigned FillOpc; + Register FillReg; + const TargetRegisterClass *FillRC; + switch (DstTy.getSizeInBits()) { + case 8: + FillOpc = Z80::SBC8ar; + FillReg = Z80::A; + FillRC = &Z80::R8RegClass; + break; + case 16: + FillOpc = Z80::SBC16aa; + FillReg = Z80::HL; + FillRC = &Z80::R16RegClass; + break; + case 24: + FillOpc = Z80::SBC24aa; + FillReg = Z80::UHL; + FillRC = &Z80::R24RegClass; + break; + default: + return false; + } + + auto Rotate = MIB.buildInstr(Z80::RRC8r, {LLT::scalar(8)}, {SrcReg}); + if (!constrainSelectedInstRegOperands(*Rotate, TII, TRI, RBI)) + return false; + auto Fill = MIB.buildInstr(FillOpc); + Fill->findRegisterUseOperand(FillReg)->setIsUndef(); + if (FillOpc == Z80::SBC8ar) + Fill.addReg(FillReg, RegState::Undef); + if (!constrainSelectedInstRegOperands(*Fill, TII, TRI, RBI)) + return false; + auto CopyFromReg = MIB.buildCopy(DstReg, FillReg); + if (!RBI.constrainGenericRegister(CopyFromReg.getReg(0), *FillRC, MRI)) + return false; + + I.eraseFromParent(); + return true; +} + +bool Z80InstructionSelector::selectZExt(MachineInstr &I, + MachineRegisterInfo &MRI) const { + assert((I.getOpcode() == TargetOpcode::G_ZEXT) && "unexpected instruction"); + + Register DstReg = I.getOperand(0).getReg(); + Register SrcReg = I.getOperand(1).getReg(); + + if (MRI.getType(SrcReg) != LLT::scalar(1)) + return false; + + MachineIRBuilder MIB(I); + + auto CopyToA = MIB.buildCopy(Z80::A, SrcReg); + if (!constrainSelectedInstRegOperands(*CopyToA, TII, TRI, RBI)) + return false; + auto And = MIB.buildInstr(Z80::AND8ai, {}, {int64_t(1)}); + if (!constrainSelectedInstRegOperands(*And, TII, TRI, RBI)) + return false; + + auto CopyFromA = MIB.buildCopy(LLT::scalar(8), Register(Z80::A)); + if (!RBI.constrainGenericRegister(CopyFromA.getReg(0), Z80::R8RegClass, MRI)) + return false; + + auto ZExt = MIB.buildZExtOrTrunc(DstReg, CopyFromA); + + I.eraseFromParent(); + return select(*ZExt); +} + +bool Z80InstructionSelector::selectAnyExt(MachineInstr &I, + MachineRegisterInfo &MRI) const { + assert((I.getOpcode() == TargetOpcode::G_ANYEXT) && "unexpected instruction"); + + const Register DstReg = I.getOperand(0).getReg(); + const Register SrcReg = I.getOperand(1).getReg(); + + const LLT DstTy = MRI.getType(DstReg); + const LLT SrcTy = MRI.getType(SrcReg); + + const RegisterBank &DstRB = *RBI.getRegBank(DstReg, MRI, TRI); + const RegisterBank &SrcRB = *RBI.getRegBank(SrcReg, MRI, TRI); + + assert(DstRB.getID() == SrcRB.getID() && + "G_ANYEXT input/output on different banks\n"); + + assert(DstTy.getSizeInBits() > SrcTy.getSizeInBits() && + "G_ANYEXT incorrect operand size"); + + const TargetRegisterClass *DstRC = getRegClass(DstTy, DstRB); + const TargetRegisterClass *SrcRC = getRegClass(SrcTy, SrcRB); + + if (!RBI.constrainGenericRegister(SrcReg, *SrcRC, MRI) || + !RBI.constrainGenericRegister(DstReg, *DstRC, MRI)) { + LLVM_DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode()) + << " operand\n"); + return false; + } + + if (SrcRC == DstRC) { + I.setDesc(TII.get(Z80::COPY)); + return true; + } + + MachineIRBuilder(I).buildInstr(TargetOpcode::SUBREG_TO_REG, {DstReg}, + {int64_t(0), SrcReg, getSubRegIndex(SrcRC)}); + I.eraseFromParent(); + return true; +} + +bool Z80InstructionSelector::selectLoadStoreOp(MachineInstr &I, + MachineRegisterInfo &MRI, + MachineFunction &MF) const { + bool IsStore = I.getOpcode() == TargetOpcode::G_STORE; + assert((IsStore || I.getOpcode() == TargetOpcode::G_LOAD) && + "unexpected instruction"); + + Register DefReg = I.getOperand(0).getReg(); + Register PtrReg = I.getOperand(1).getReg(); + MachineInstr *PtrMI = MRI.getVRegDef(PtrReg); + LLT Ty = MRI.getType(DefReg); + + I.RemoveOperand(1); + if (IsStore) + I.RemoveOperand(0); + MachineInstrBuilder MIB(MF, I); + bool IsOff = false; + int8_t Off = 0; + if (PtrMI) { + switch (PtrMI->getOpcode()) { + case TargetOpcode::G_FRAME_INDEX: + IsOff = true; + break; + case TargetOpcode::G_PTR_ADD: + if (auto OffConst = + getConstantVRegVal(PtrMI->getOperand(2).getReg(), MRI)) { + if (isInt<8>(*OffConst)) { + IsOff = true; + Off = *OffConst; + if (MachineInstr *BaseMI = + MRI.getVRegDef(PtrMI->getOperand(1).getReg())) + if (BaseMI->getOpcode() == TargetOpcode::G_FRAME_INDEX) + PtrMI = BaseMI; + } + } + break; + } + } + unsigned Opc; + switch (Ty.getSizeInBits()) { + case 8: + Opc = IsOff ? IsStore ? Z80::LD8og : Z80::LD8go + : IsStore ? Z80::LD8pg : Z80::LD8gp; + break; + case 16: + Opc = STI.has16BitEZ80Ops() ? IsOff ? IsStore ? Z80::LD16or : Z80::LD16ro + : IsStore ? Z80::LD16pr : Z80::LD16rp + : IsOff ? IsStore ? Z80::LD88or : Z80::LD88ro + : IsStore ? Z80::LD88pr : Z80::LD88rp; + break; + case 24: + assert(STI.is24Bit() && "Illegal memory access size."); + Opc = IsOff ? IsStore ? Z80::LD24or : Z80::LD24ro + : IsStore ? Z80::LD24pr : Z80::LD24rp; + break; + default: + return false; + } + I.setDesc(TII.get(Opc)); + if (IsOff) + MIB.add(PtrMI->getOperand(1)).addImm(Off); + else + MIB.addReg(PtrReg); + if (IsStore) + MIB.addReg(DefReg); + + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); +} + +bool Z80InstructionSelector::selectFrameIndexOrGep(MachineInstr &I, + MachineRegisterInfo &MRI, + MachineFunction &MF) const { + bool Is24Bit = STI.is24Bit(); + bool HasLEA = STI.hasEZ80Ops(); + unsigned Opc = I.getOpcode(); + assert( + (Opc == TargetOpcode::G_FRAME_INDEX || Opc == TargetOpcode::G_PTR_ADD) && + "unexpected instruction"); + + if (Opc == TargetOpcode::G_PTR_ADD) { + auto Off = getConstantVRegVal(I.getOperand(2).getReg(), MRI); + if (Off && *Off >= -1 && *Off <= 1) { + I.RemoveOperand(2); + if (!*Off) { + I.setDesc(TII.get(TargetOpcode::COPY)); + return selectCopy(I, MRI); + } + I.setDesc(TII.get(*Off == 1 ? Is24Bit ? Z80::INC24r : Z80::INC16r + : Is24Bit ? Z80::DEC24r : Z80::DEC16r)); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); + } + if (!HasLEA || !Off || !isInt<8>(*Off)) { + I.setDesc(TII.get(Is24Bit ? Z80::ADD24ao : Z80::ADD16ao)); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); + } + I.getOperand(2).ChangeToImmediate(*Off); + } + + // Use LEA to calculate frame index and GEP + I.setDesc(TII.get(Is24Bit ? Z80::LEA24ro : Z80::LEA16ro)); + + // Make a note if this LEA is illegal. + if (!HasLEA) + MF.getInfo()->setHasIllegalLEA(); + + if (Opc == TargetOpcode::G_FRAME_INDEX) + MachineInstrBuilder(MF, I).addImm(0); + + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); +} + +bool Z80InstructionSelector::selectUnmergeValues(MachineInstr &I, + MachineRegisterInfo &MRI, + MachineFunction &MF) const { + assert((I.getOpcode() == TargetOpcode::G_UNMERGE_VALUES) && + "unexpected instruction"); + + switch (MRI.getType(I.getOperand(I.getNumOperands() - 1).getReg()) + .getSizeInBits()) { + case 16: + assert(I.getNumOperands() == 3 && + MRI.getType(I.getOperand(0).getReg()) == LLT::scalar(8) && + MRI.getType(I.getOperand(1).getReg()) == LLT::scalar(8) && + "Illegal instruction"); + BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(TargetOpcode::COPY)) + .add(I.getOperand(0)) + .addReg(I.getOperand(2).getReg(), 0, Z80::sub_low); + BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(TargetOpcode::COPY)) + .add(I.getOperand(1)) + .addReg(I.getOperand(2).getReg(), 0, Z80::sub_high); + if (!RBI.constrainGenericRegister(I.getOperand(0).getReg(), Z80::R8RegClass, + MRI) || + !RBI.constrainGenericRegister(I.getOperand(1).getReg(), Z80::R8RegClass, + MRI) || + !RBI.constrainGenericRegister( + I.getOperand(2).getReg(), + *(STI.is24Bit() ? &Z80::R16RegClass : &Z80::G16RegClass), MRI)) + return false; + break; + case 24: { + assert(STI.is24Bit() && "Illegal memory access size."); + assert(I.getNumOperands() == 4 && + MRI.getType(I.getOperand(0).getReg()) == LLT::scalar(8) && + MRI.getType(I.getOperand(1).getReg()) == LLT::scalar(8) && + MRI.getType(I.getOperand(2).getReg()) == LLT::scalar(8) && + "Illegal instruction"); + int FI = MF.getFrameInfo().CreateStackObject(3, Align(1), false); + BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Z80::LD24or)) + .addFrameIndex(FI) + .addImm(0) + .add(I.getOperand(3)); + BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Z80::LD8ro)) + .add(I.getOperand(2)) + .addFrameIndex(FI) + .addImm(2); + BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(TargetOpcode::COPY)) + .add(I.getOperand(1)) + .addReg(I.getOperand(3).getReg(), 0, Z80::sub_high); + BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(TargetOpcode::COPY)) + .add(I.getOperand(0)) + .addReg(I.getOperand(3).getReg(), 0, Z80::sub_low); + if (!RBI.constrainGenericRegister(I.getOperand(0).getReg(), Z80::R8RegClass, + MRI) || + !RBI.constrainGenericRegister(I.getOperand(1).getReg(), Z80::R8RegClass, + MRI) || + !RBI.constrainGenericRegister(I.getOperand(2).getReg(), Z80::R8RegClass, + MRI) || + !RBI.constrainGenericRegister(I.getOperand(3).getReg(), + Z80::R24RegClass, MRI)) + return false; + break; + } + default: + llvm_unreachable("Illegal instruction"); + } + I.eraseFromParent(); + return true; +} + +bool Z80InstructionSelector::selectMergeValues(MachineInstr &I, + MachineRegisterInfo &MRI, + MachineFunction &MF) const { + assert((I.getOpcode() == TargetOpcode::G_MERGE_VALUES) && + "unexpected instruction"); + Register DstReg = I.getOperand(0).getReg(); + MachineInstr *NewI; + switch (MRI.getType(DstReg).getSizeInBits()) { + case 16: + assert(I.getNumOperands() == 3 && + MRI.getType(I.getOperand(1).getReg()) == LLT::scalar(8) && + MRI.getType(I.getOperand(2).getReg()) == LLT::scalar(8) && + "Illegal instruction"); + + NewI = BuildMI(*I.getParent(), I, I.getDebugLoc(), + TII.get(TargetOpcode::REG_SEQUENCE), DstReg) + .add(I.getOperand(1)) + .addImm(Z80::sub_low) + .add(I.getOperand(2)) + .addImm(Z80::sub_high); + if (!RBI.constrainGenericRegister( + DstReg, *(STI.is24Bit() ? &Z80::R16RegClass : &Z80::G16RegClass), + MRI)) + return false; + break; + case 24: { + assert(STI.is24Bit() && "Illegal memory access size."); + assert(I.getNumOperands() == 4 && + MRI.getType(I.getOperand(1).getReg()) == LLT::scalar(8) && + MRI.getType(I.getOperand(2).getReg()) == LLT::scalar(8) && + MRI.getType(I.getOperand(3).getReg()) == LLT::scalar(8) && + "Illegal instruction"); + int FI = MF.getFrameInfo().CreateStackObject(1, Align(1), false); + auto Store = + BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Z80::LD8or)) + .addFrameIndex(FI) + .addImm(0) + .add(I.getOperand(3)); + if (!constrainSelectedInstRegOperands(*Store, TII, TRI, RBI)) + return false; + Register Temp1Reg = MRI.createVirtualRegister(&Z80::R24RegClass); + Register Temp2Reg = MRI.createVirtualRegister(&Z80::R24RegClass); + BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Z80::LD24ro), Temp1Reg) + .addFrameIndex(FI) + .addImm(-2); + BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Z80::INSERT_SUBREG), + Temp2Reg) + .addReg(Temp1Reg) + .add(I.getOperand(2)) + .addImm(Z80::sub_high); + NewI = BuildMI(*I.getParent(), I, I.getDebugLoc(), + TII.get(Z80::INSERT_SUBREG), DstReg) + .addReg(Temp2Reg) + .add(I.getOperand(1)) + .addImm(Z80::sub_low); + if (!RBI.constrainGenericRegister(DstReg, Z80::R24RegClass, MRI)) + return false; + break; + } + default: + llvm_unreachable("Illegal instruction"); + } + if (!constrainSelectedInstRegOperands(*NewI, TII, TRI, RBI)) + return false; + I.eraseFromParent(); + return true; +} + +Z80::CondCode +Z80InstructionSelector::foldCompare(MachineInstr &I, MachineIRBuilder &MIB, + MachineRegisterInfo &MRI) const { + assert(I.getOpcode() == TargetOpcode::G_ICMP && "unexpected instruction"); + const Function &F = MIB.getMF().getFunction(); + bool OptSize = F.hasOptSize(); + + auto Pred = CmpInst::Predicate(I.getOperand(1).getPredicate()); + Register LHSReg = I.getOperand(2).getReg(); + Register RHSReg = I.getOperand(3).getReg(); + LLT OpTy = MRI.getType(LHSReg); + unsigned OpSize = OpTy.getSizeInBits(); + assert(MRI.getType(I.getOperand(0).getReg()) == LLT::scalar(1) && + !OpTy.isVector() && MRI.getType(RHSReg) == OpTy && "Unexpected type"); + + bool IsSigned, IsSwapped, IsConst; + Z80::CondCode CC = + Z80::GetBranchConditionForPredicate(Pred, IsSigned, IsSwapped, IsConst); + if (IsSwapped) + std::swap(LHSReg, RHSReg); + + unsigned Opc, LDIOpc, AddOpc; + Register Reg; + switch (OpSize) { + case 8: + Opc = Z80::CP8ar; + Reg = Z80::A; + break; + case 16: + Opc = Z80::SUB16ao; + LDIOpc = Z80::LD16ri; + AddOpc = Z80::ADD16ao; + Reg = Z80::HL; + break; + case 24: + Opc = Z80::SUB24ao; + LDIOpc = Z80::LD24ri; + AddOpc = Z80::ADD24ao; + Reg = Z80::UHL; + break; + default: + return Z80::COND_INVALID; + } + + if (IsSigned && !OptSize) { + int64_t Off = 1 << (OpSize - 1); + if (OpSize == 8) { + auto CopyRHSToA = MIB.buildCopy(Register(Z80::A), RHSReg); + if (!constrainSelectedInstRegOperands(*CopyRHSToA, TII, TRI, RBI)) + return Z80::COND_INVALID; + auto AddRHS = MIB.buildInstr(Z80::ADD8ai, {}, {Off}); + if (!constrainSelectedInstRegOperands(*AddRHS, TII, TRI, RBI)) + return Z80::COND_INVALID; + auto CopyRHSFromA = MIB.buildCopy(OpTy, Register(Z80::A)); + if (!constrainSelectedInstRegOperands(*CopyRHSFromA, TII, TRI, RBI)) + return Z80::COND_INVALID; + RHSReg = CopyRHSFromA.getReg(0); + auto CopyLHSToA = MIB.buildCopy(Register(Z80::A), LHSReg); + if (!constrainSelectedInstRegOperands(*CopyLHSToA, TII, TRI, RBI)) + return Z80::COND_INVALID; + auto AddLHS = MIB.buildInstr(Z80::ADD8ai, {}, {Off}); + if (!constrainSelectedInstRegOperands(*AddLHS, TII, TRI, RBI)) + return Z80::COND_INVALID; + auto CopyLHSFromA = MIB.buildCopy(OpTy, Register(Z80::A)); + if (!constrainSelectedInstRegOperands(*CopyLHSFromA, TII, TRI, RBI)) + return Z80::COND_INVALID; + LHSReg = CopyLHSFromA.getReg(0); + if (!RBI.constrainGenericRegister(LHSReg, Z80::R8RegClass, MRI)) + return Z80::COND_INVALID; + } else { + auto LDI = MIB.buildInstr(LDIOpc, {OpTy}, {Off}); + if (!constrainSelectedInstRegOperands(*LDI, TII, TRI, RBI)) + return Z80::COND_INVALID; + auto AddLHS = MIB.buildInstr(AddOpc, {OpTy}, {LHSReg, LDI}); + if (!constrainSelectedInstRegOperands(*AddLHS, TII, TRI, RBI)) + return Z80::COND_INVALID; + LHSReg = AddLHS.getReg(0); + auto AddRHS = MIB.buildInstr(AddOpc, {OpTy}, {RHSReg, LDI}); + if (!constrainSelectedInstRegOperands(*AddRHS, TII, TRI, RBI)) + return Z80::COND_INVALID; + RHSReg = AddRHS.getReg(0); + } + switch (CC) { + default: + llvm_unreachable("Expected signed condition"); + case Z80::COND_P: + CC = Z80::COND_NC; + break; + case Z80::COND_M: + CC = Z80::COND_C; + break; + } + } + + auto Copy = MIB.buildCopy(Reg, LHSReg); + if (!constrainSelectedInstRegOperands(*Copy, TII, TRI, RBI)) + return Z80::COND_INVALID; + auto Cmp = MIB.buildInstr(Opc, {}, {RHSReg}); + if (!constrainSelectedInstRegOperands(*Cmp, TII, TRI, RBI)) + return Z80::COND_INVALID; + if (IsSigned && OptSize) { + LLT s8 = LLT::scalar(8); + Type *Int8Ty = Type::getInt8Ty(F.getContext()); + Register FlagsReg = MIB.buildCopy(s8, Register(Z80::F)).getReg(0); + CallLowering::ArgInfo FlagsArg(FlagsReg, Int8Ty); + Register SignedFlagsReg = MRI.createGenericVirtualRegister(s8); + CallLowering::ArgInfo SignedFlagsArg(SignedFlagsReg, Int8Ty); + createLibcall(MIB, RTLIB::SCMP, SignedFlagsArg, FlagsArg); + MIB.buildCopy(Register(Z80::F), SignedFlagsReg); + if (!RBI.constrainGenericRegister(FlagsReg, Z80::F8RegClass, MRI) || + !RBI.constrainGenericRegister(SignedFlagsReg, Z80::F8RegClass, MRI)) + return Z80::COND_INVALID; + } + return CC; +} + +Z80::CondCode Z80InstructionSelector::foldExtendedAddSub( + MachineInstr &I, MachineIRBuilder &MIB, MachineRegisterInfo &MRI) const { + unsigned Opc = I.getOpcode(); + assert((Opc == TargetOpcode::G_UADDO || Opc == TargetOpcode::G_UADDE || + Opc == TargetOpcode::G_USUBO || Opc == TargetOpcode::G_USUBE || + Opc == TargetOpcode::G_SADDO || Opc == TargetOpcode::G_SADDE || + Opc == TargetOpcode::G_SSUBO || Opc == TargetOpcode::G_SSUBE) && + "unexpected instruction"); + + bool IsAdd = Opc == TargetOpcode::G_UADDO || Opc == TargetOpcode::G_UADDE || + Opc == TargetOpcode::G_SADDO || Opc == TargetOpcode::G_SADDE; + bool IsExtend = Opc == TargetOpcode::G_UADDE || + Opc == TargetOpcode::G_USUBE || + Opc == TargetOpcode::G_SADDE || Opc == TargetOpcode::G_SSUBE; + bool IsSigned = Opc == TargetOpcode::G_SADDO || + Opc == TargetOpcode::G_SADDE || + Opc == TargetOpcode::G_SSUBO || Opc == TargetOpcode::G_SSUBE; + + Register DstReg = I.getOperand(0).getReg(); + Register LHSReg = I.getOperand(2).getReg(); + Register RHSReg = I.getOperand(3).getReg(); + LLT OpTy = MRI.getType(DstReg); + + unsigned AddSubOpc; + Register AddSubReg; + const TargetRegisterClass *AddSubRC; + switch (OpTy.getSizeInBits()) { + case 8: + AddSubOpc = IsAdd ? Z80::ADC8ar : Z80::SBC8ar; + AddSubReg = Z80::A; + AddSubRC = &Z80::R8RegClass; + break; + case 16: + AddSubOpc = IsAdd ? Z80::ADC16ao : Z80::SBC16ao; + AddSubReg = Z80::HL; + AddSubRC = &Z80::R16RegClass; + break; + case 24: + AddSubOpc = IsAdd ? Z80::ADC24ao : Z80::SBC24ao; + AddSubReg = Z80::UHL; + AddSubRC = &Z80::R24RegClass; + break; + default: + return Z80::COND_INVALID; + } + + if (IsExtend) { + Register CarryInReg = I.getOperand(4).getReg(); + Z80::CondCode CC = foldCond(CarryInReg, MIB, MRI); + if (CC == Z80::COND_INVALID) + return CC; + if (CC != Z80::COND_C) { + auto SetCC = MIB.buildInstr(Z80::SetCC, {LLT::scalar(1)}, {int64_t(CC)}); + if (!RBI.constrainGenericRegister(SetCC.getReg(0), Z80::R8RegClass, MRI)) + return Z80::COND_INVALID; + auto BitI = MIB.buildInstr(Z80::RRC8r, {LLT::scalar(8)}, {SetCC}); + if (!constrainSelectedInstRegOperands(*BitI, TII, TRI, RBI)) + return Z80::COND_INVALID; + if (!select(*SetCC)) + return Z80::COND_INVALID; + } + } else + MIB.buildInstr(Z80::RCF); + MIB.buildCopy(AddSubReg, LHSReg); + if (!RBI.constrainGenericRegister(LHSReg, *AddSubRC, MRI)) + return Z80::COND_INVALID; + auto AddSubI = MIB.buildInstr(AddSubOpc, {}, {RHSReg}); + if (!constrainSelectedInstRegOperands(*AddSubI, TII, TRI, RBI)) + return Z80::COND_INVALID; + if (MIB.getInsertPt() == I) { + MIB.buildCopy(DstReg, AddSubReg); + if (!RBI.constrainGenericRegister(DstReg, *AddSubRC, MRI)) + return Z80::COND_INVALID; + } + return IsSigned ? Z80::COND_PE : Z80::COND_C; +} + +Z80::CondCode +Z80InstructionSelector::foldSetCC(MachineInstr &I, MachineIRBuilder &MIB, + MachineRegisterInfo &MRI) const { + assert(I.getOpcode() == Z80::SetCC && "unexpected instruction"); + auto CC = Z80::CondCode(I.getOperand(1).getImm()); + + if (I == MIB.getInsertPt()) + return CC; + + return Z80::COND_INVALID; +} + +Z80::CondCode Z80InstructionSelector::foldCond(Register CondReg, + MachineIRBuilder &MIB, + MachineRegisterInfo &MRI) const { + assert(MRI.getType(CondReg) == LLT::scalar(1) && "Expected s1 condition"); + + MachineInstr *CondDef = nullptr; + while (MachineInstr *LookthroughDef = MRI.getVRegDef(CondReg)) { + //if (LookthroughDef != MIB.getInsertPt() && !MRI.hasOneUse(CondReg)) + // break; + CondDef = LookthroughDef; + unsigned Opc = CondDef->getOpcode(); + if (Opc != TargetOpcode::COPY && Opc != TargetOpcode::G_TRUNC) + break; + Register SrcReg = CondDef->getOperand(1).getReg(); + if (SrcReg.isPhysical()) + break; + CondReg = SrcReg; + } + + Z80::CondCode CC = Z80::COND_INVALID; + if (CondDef && MIB.getInsertPt() == *CondDef) { + switch (CondDef->getOpcode()) { + case TargetOpcode::G_ICMP: + CC = foldCompare(*CondDef, MIB, MRI); + break; + case TargetOpcode::G_UADDO: + case TargetOpcode::G_UADDE: + case TargetOpcode::G_USUBO: + case TargetOpcode::G_USUBE: + case TargetOpcode::G_SADDO: + case TargetOpcode::G_SADDE: + case TargetOpcode::G_SSUBO: + case TargetOpcode::G_SSUBE: + CC = foldExtendedAddSub(*CondDef, MIB, MRI); + break; + case Z80::SetCC: + CC = foldSetCC(*CondDef, MIB, MRI); + break; + default: + break; + } + } + + if (CC == Z80::COND_INVALID) { + // Fallback to bit test + auto BitI = MIB.buildInstr(Z80::BIT8bg, {}, {int64_t(0), CondReg}); + if (constrainSelectedInstRegOperands(*BitI, TII, TRI, RBI)) + CC = Z80::COND_NZ; + } + + return CC; +} + +bool Z80InstructionSelector::selectSetCond(MachineInstr &I, + MachineRegisterInfo &MRI) const { + Register CondReg = I.getOperand(I.getNumExplicitDefs() - 1).getReg(); + assert(MRI.getType(CondReg) == LLT::scalar(1) && "Expected s1 condition"); + + MachineIRBuilder MIB(I); + Z80::CondCode CC = foldCond(CondReg, MIB, MRI); + if (CC == Z80::COND_INVALID) + return false; + + if (MRI.reg_empty(CondReg)) + return true; + + auto TrueI = MIB.buildInstr(Z80::LD8ri, {LLT::scalar(8)}, {int64_t(1)}); + auto FalseI = MIB.buildInstr(Z80::LD8ri, {LLT::scalar(8)}, {int64_t(0)}); + auto SelectI = + MIB.buildInstr(Z80::Select8, {CondReg}, {TrueI, FalseI, int64_t(CC)}); + I.eraseFromParent(); + return constrainSelectedInstRegOperands(*FalseI, TII, TRI, RBI) && + constrainSelectedInstRegOperands(*TrueI, TII, TRI, RBI) && + constrainSelectedInstRegOperands(*SelectI, TII, TRI, RBI); +} + +bool Z80InstructionSelector::selectSelect(MachineInstr &I, + MachineRegisterInfo &MRI) const { + assert(I.getOpcode() == TargetOpcode::G_SELECT && "unexpected instruction"); + + LLT OpTy = MRI.getType(I.getOperand(2).getReg()); + unsigned Select; + const TargetRegisterClass *RC; + switch (OpTy.getSizeInBits()) { + case 8: + Select = Z80::Select8; + RC = &Z80::R8RegClass; + break; + case 16: + Select = Z80::Select16; + RC = &Z80::R16RegClass; + break; + case 24: + Select = Z80::Select24; + RC = &Z80::R24RegClass; + break; + default: + return false; + } + + MachineIRBuilder MIB(I); + Z80::CondCode CC = foldCond(I.getOperand(1).getReg(), MIB, MRI); + if (CC == Z80::COND_INVALID) + return false; + + I.setDesc(TII.get(Select)); + I.getOperand(1).setReg(I.getOperand(2).getReg()); + I.getOperand(2).setReg(I.getOperand(3).getReg()); + I.getOperand(3).ChangeToImmediate(CC); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); +} + +bool Z80InstructionSelector::selectBrCond(MachineInstr &I, + MachineRegisterInfo &MRI) const { + assert(I.getOpcode() == TargetOpcode::G_BRCOND && "unexpected instruction"); + + MachineIRBuilder MIB(I); + Z80::CondCode CC = foldCond(I.getOperand(0).getReg(), MIB, MRI); + if (CC == Z80::COND_INVALID) + return false; + + MIB.buildInstr(Z80::JQCC).add(I.getOperand(1)).addImm(CC); + I.eraseFromParent(); + return true; +} + +bool Z80InstructionSelector::selectBrJT(MachineInstr &I, + MachineRegisterInfo &MRI, + MachineFunction &MF) const { + assert((I.getOpcode() == TargetOpcode::G_BRJT) && "unexpected instruction"); + + unsigned EntrySize = MF.getJumpTableInfo()->getEntrySize(MF.getDataLayout()); + assert(EntrySize && EntrySize <= 3 && + "Jump table entry size is expected to be less than pointer size"); + + Register AddrReg = I.getOperand(0).getReg(); + LLT AddrTy = MRI.getType(AddrReg); + Register IndexReg = I.getOperand(2).getReg(); + MachineIRBuilder MIB(I); + + for (unsigned Factor = 0; Factor != EntrySize; ++Factor) { + auto Add = MIB.buildInstr(STI.is24Bit() ? Z80::ADD24ao : Z80::ADD16ao, + {AddrTy}, {AddrReg, IndexReg}); + if (!constrainSelectedInstRegOperands(*Add, TII, TRI, RBI)) + return false; + AddrReg = Add.getReg(0); + } + + auto Load = MIB.buildInstr(STI.is24Bit() + ? Z80::LD24rp + : STI.hasEZ80Ops() ? Z80::LD16rp : Z80::LD88rp, + {AddrTy}, {AddrReg}); + if (!constrainSelectedInstRegOperands(*Load, TII, TRI, RBI)) + return false; + + auto Jump = + MIB.buildInstr(STI.is24Bit() ? Z80::JP24r : Z80::JP16r, {}, {Load}); + if (!constrainSelectedInstRegOperands(*Jump, TII, TRI, RBI)) + return false; + + I.eraseFromParent(); + return true; +} + +bool Z80InstructionSelector::selectImplicitDefOrPHI( + MachineInstr &I, MachineRegisterInfo &MRI) const { + assert((I.getOpcode() == TargetOpcode::G_IMPLICIT_DEF || + I.getOpcode() == TargetOpcode::G_PHI) && + "unexpected instruction"); + + Register DstReg = I.getOperand(0).getReg(); + + if (!MRI.getRegClassOrNull(DstReg)) { + const LLT DstTy = MRI.getType(DstReg); + const TargetRegisterClass *RC = getRegClass(DstTy, DstReg, MRI); + + if (!RBI.constrainGenericRegister(DstReg, *RC, MRI)) + return false; + } + + if (I.getOpcode() == TargetOpcode::G_IMPLICIT_DEF) + I.setDesc(TII.get(Z80::IMPLICIT_DEF)); + else + I.setDesc(TII.get(Z80::PHI)); + + return true; +} + +InstructionSelector::ComplexRendererFns +Z80InstructionSelector::selectMem(MachineOperand &MO) const { + llvm_unreachable("Unimplemented!"); +} + +InstructionSelector::ComplexRendererFns +Z80InstructionSelector::selectOff(MachineOperand &MO) const { + llvm_unreachable("Unimplemented!"); +} + +InstructionSelector * +llvm::createZ80InstructionSelector(const Z80TargetMachine &TM, + Z80Subtarget &Subtarget, + Z80RegisterBankInfo &RBI) { + return new Z80InstructionSelector(TM, Subtarget, RBI); +} diff --git a/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.cpp b/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.cpp new file mode 100644 index 00000000000000..a72f9e522664f2 --- /dev/null +++ b/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.cpp @@ -0,0 +1,469 @@ +//===- Z80LegalizerInfo.cpp --------------------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the targeting of the Machinelegalizer class for Z80. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#include "Z80LegalizerInfo.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "Z80MachineFunctionInfo.h" +#include "Z80Subtarget.h" +#include "Z80TargetMachine.h" +#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h" +#include +using namespace llvm; +using namespace TargetOpcode; +using namespace LegalizeActions; + +Z80LegalizerInfo::Z80LegalizerInfo(const Z80Subtarget &STI, + const Z80TargetMachine &TM) + : Subtarget(STI), TM(TM) { + bool Is24Bit = Subtarget.is24Bit(); + LLT p0 = LLT::pointer(0, TM.getPointerSizeInBits(0)); + LLT s1 = LLT::scalar(1); + LLT s8 = LLT::scalar(8); + LLT s16 = LLT::scalar(16); + LLT s24 = LLT::scalar(24); + LLT s32 = LLT::scalar(32); + LLT s64 = LLT::scalar(64); + LLT sMax = Is24Bit ? s24 : s16; + auto LegalTypes24 = {p0, s8, s16, s24}, LegalTypes16 = {p0, s8, s16}; + auto LegalTypes = Is24Bit ? LegalTypes24 : LegalTypes16; + auto LegalScalars24 = {s8, s16, s24}, LegalScalars16 = {s8, s16}; + auto LegalScalars = Is24Bit ? LegalScalars24 : LegalScalars16; + auto LegalLibcallScalars24 = {s8, s16, s24, s32, s64}; + auto LegalLibcallScalars16 = {s8, s16, s32, s64}; + auto LegalLibcallScalars = + Is24Bit ? LegalLibcallScalars24 : LegalLibcallScalars16; + auto NotMax24 = {s8, s16}, NotMax16 = {s8}; + auto NotMax = Is24Bit ? NotMax24 : NotMax16; + auto NotMin24 = {s16, s24}, NotMin16 = {s16}; + auto NotMin = Is24Bit ? NotMin24 : NotMin16; + auto NotMaxWithOne24 = {s1, s8, s16}, NotMaxWithOne16 = {s1, s8}; + auto NotMaxWithOne = Is24Bit ? NotMaxWithOne24 : NotMaxWithOne16; + + getActionDefinitionsBuilder(G_MERGE_VALUES) + .legalForCartesianProduct(NotMin, NotMax) + .clampScalar(0, *NotMin.begin(), *std::prev(NotMin.end())) + .clampScalar(1, *NotMax.begin(), *std::prev(NotMax.end())); + + getActionDefinitionsBuilder(G_UNMERGE_VALUES) + .legalForCartesianProduct(NotMax, NotMin) + .clampScalar(1, *NotMin.begin(), *std::prev(NotMin.end())) + .clampScalar(0, *NotMax.begin(), *std::prev(NotMax.end())); + + getActionDefinitionsBuilder({G_ZEXT, G_ANYEXT}) + .legalForCartesianProduct(LegalScalars, NotMaxWithOne) + .clampScalar(0, *LegalScalars.begin(), *std::prev(LegalScalars.end())) + .clampScalar(1, *NotMaxWithOne.begin(), *std::prev(NotMaxWithOne.end())); + + getActionDefinitionsBuilder(G_SEXT) + .legalForCartesianProduct(LegalScalars, {s1}) + .maxScalar(0, sMax) + .maxScalar(0, s8) + .maxScalar(1, s8); + + getActionDefinitionsBuilder(G_TRUNC) + .legalForCartesianProduct(NotMaxWithOne, LegalScalars) + .clampScalar(1, *LegalScalars.begin(), *std::prev(LegalScalars.end())) + .clampScalar(0, *NotMaxWithOne.begin(), *std::prev(NotMaxWithOne.end())); + + getActionDefinitionsBuilder({G_FREEZE, G_IMPLICIT_DEF, G_PHI, G_CONSTANT}) + .legalFor(LegalTypes) + .clampScalar(0, s8, sMax); + + getActionDefinitionsBuilder(G_FCONSTANT) + .customFor({s32, s64}); + + getActionDefinitionsBuilder(G_INTTOPTR) + .legalFor({{p0, sMax}}) + .clampScalar(1, sMax, sMax); + + getActionDefinitionsBuilder(G_PTRTOINT) + .legalFor({{sMax, p0}}) + .clampScalar(0, sMax, sMax); + + getActionDefinitionsBuilder(G_PTR_ADD) + .legalForCartesianProduct({p0}, LegalScalars) + .clampScalar(1, s8, sMax); + + getActionDefinitionsBuilder({G_ADD, G_SUB}) + .legalFor(LegalScalars) + .libcallFor({s32, s64}) + .clampScalar(0, s8, sMax); + + getActionDefinitionsBuilder({G_UADDO, G_UADDE, G_USUBO, G_USUBE, + G_SADDO, G_SADDE, G_SSUBO, G_SSUBE}) + .legalForCartesianProduct(LegalScalars, {s1}) + .clampScalar(0, s8, sMax); + + { + auto &&Mul = getActionDefinitionsBuilder(G_MUL); + if (Subtarget.hasZ180Ops()) + Mul.legalFor({s8}); + Mul.libcallFor(LegalLibcallScalars) + .clampScalar(0, s8, s32); + } + + getActionDefinitionsBuilder({G_SDIV, G_UDIV, G_SREM, G_UREM}) + .libcallFor(LegalLibcallScalars) + .clampScalar(0, s8, s32); + + getActionDefinitionsBuilder({G_AND, G_OR, G_XOR}) + .legalFor({s8}) + .customFor(LegalLibcallScalars) + .clampScalar(0, s8, s32); + + getActionDefinitionsBuilder({G_SHL, G_LSHR, G_ASHR}) + .libcallForCartesianProduct(LegalLibcallScalars, {s8}) + .clampScalar(1, s8, s8) + .clampScalar(0, s8, s64); + + getActionDefinitionsBuilder({G_FSHL, G_FSHR}) + .custom(); + + getActionDefinitionsBuilder({G_FADD, G_FSUB, G_FMUL, G_FDIV, G_FREM, G_FNEG, + G_FABS, G_INTRINSIC_TRUNC, G_INTRINSIC_ROUND, + G_FCEIL, G_FCOS, G_FSIN, G_FSQRT, G_FFLOOR, + G_FRINT, G_FNEARBYINT}) + .libcallFor({s32, s64}); + + getActionDefinitionsBuilder(G_FPTRUNC) + .libcallFor({{s32, s64}}); + + getActionDefinitionsBuilder(G_FPEXT) + .libcallFor({{s64, s32}}); + + getActionDefinitionsBuilder({G_FPTOSI, G_FPTOUI}) + .libcallForCartesianProduct({s32, s64}, {s32, s64}) + .clampScalar(0, s32, s64); + + getActionDefinitionsBuilder({G_SITOFP, G_UITOFP}) + .libcallForCartesianProduct({s32, s64}, {s32, s64}) + .clampScalar(1, s32, s64); + + getActionDefinitionsBuilder({G_LOAD, G_STORE}) + .legalForCartesianProduct(LegalTypes, {p0}) + .clampScalar(0, s8, sMax); + for (unsigned MemOp : {G_LOAD, G_STORE}) + setLegalizeScalarToDifferentSizeStrategy(MemOp, 0, + narrowToSmallerAndWidenToSmallest); + + getActionDefinitionsBuilder( + {G_FRAME_INDEX, G_GLOBAL_VALUE, G_BRINDIRECT, G_JUMP_TABLE}) + .legalFor({p0}); + + getActionDefinitionsBuilder(G_VASTART) + .customFor({p0}); + + getActionDefinitionsBuilder(G_ICMP) + .legalForCartesianProduct({s1}, LegalTypes) + .customForCartesianProduct({s1}, {s32, s64}) + .clampScalar(1, s8, s32); + + getActionDefinitionsBuilder(G_FCMP) + .customForCartesianProduct({s1}, {s32, s64}); + + getActionDefinitionsBuilder(G_BRCOND) + .legalFor({s1}); + + getActionDefinitionsBuilder(G_BRJT) + .legalForCartesianProduct({p0}, LegalScalars) + .clampScalar(1, s8, sMax); + + getActionDefinitionsBuilder(G_SELECT) + .legalForCartesianProduct(LegalTypes, {s1}) + .clampScalar(0, s8, sMax); + + getActionDefinitionsBuilder({G_DYN_STACKALLOC, G_CTLZ_ZERO_UNDEF, + G_CTTZ_ZERO_UNDEF, G_CTLZ, G_CTTZ, G_BSWAP, + G_SMULO, G_UMULO, G_SMULH, G_UMULH, + G_UADDSAT, G_SADDSAT, G_USUBSAT, G_SSUBSAT}) + .lower(); + + getActionDefinitionsBuilder(G_CTPOP) + .libcallForCartesianProduct({s8}, LegalLibcallScalars) + .clampScalar(0, s8, s8); + + getActionDefinitionsBuilder(G_BITREVERSE) + .libcallFor(LegalLibcallScalars) + .clampScalar(0, s8, s64); + + computeTables(); + verify(*STI.getInstrInfo()); +} + +LegalizerHelper::LegalizeResult +Z80LegalizerInfo::legalizeCustomMaybeLegal(LegalizerHelper &Helper, + MachineInstr &MI) const { + Helper.MIRBuilder.setInstr(MI); + switch (MI.getOpcode()) { + default: + // No idea what to do. + return LegalizerHelper::UnableToLegalize; + case TargetOpcode::G_AND: + case TargetOpcode::G_OR: + case TargetOpcode::G_XOR: + return legalizeBitwise(Helper, MI); + case TargetOpcode::G_FCONSTANT: + return legalizeFConstant(Helper, MI); + case TargetOpcode::G_VASTART: + return legalizeVAStart(Helper, MI); + case TargetOpcode::G_FSHL: + case TargetOpcode::G_FSHR: + return legalizeFunnelShift(Helper, MI); + case TargetOpcode::G_ICMP: + case TargetOpcode::G_FCMP: + return legalizeCompare(Helper, MI); + } +} + +LegalizerHelper::LegalizeResult +Z80LegalizerInfo::legalizeBitwise(LegalizerHelper &Helper, + MachineInstr &MI) const { + assert((MI.getOpcode() == TargetOpcode::G_AND || + MI.getOpcode() == TargetOpcode::G_OR || + MI.getOpcode() == TargetOpcode::G_XOR) && + "Unexpected opcode"); + if (!MI.getParent()->getParent()->getFunction().hasOptSize() && + Helper.MIRBuilder.getMRI()->getType(MI.getOperand(0).getReg()) == + LLT::scalar(16)) + if (Helper.narrowScalar(MI, 0, LLT::scalar(8)) == + LegalizerHelper::Legalized) + return LegalizerHelper::Legalized; + return Helper.libcall(MI); +} + +LegalizerHelper::LegalizeResult +Z80LegalizerInfo::legalizeFConstant(LegalizerHelper &Helper, + MachineInstr &MI) const { + assert(MI.getOpcode() == TargetOpcode::G_FCONSTANT && "Unexpected opcode"); + Helper.Observer.changingInstr(MI); + MI.setDesc(Helper.MIRBuilder.getTII().get(TargetOpcode::G_CONSTANT)); + MachineOperand &Imm = MI.getOperand(1); + const ConstantFP *FPImm = Imm.getFPImm(); + Imm.ChangeToCImmediate(ConstantInt::get( + FPImm->getContext(), FPImm->getValueAPF().bitcastToAPInt())); + Helper.Observer.changedInstr(MI); + return LegalizerHelper::Legalized; +} + +LegalizerHelper::LegalizeResult +Z80LegalizerInfo::legalizeVAStart(LegalizerHelper &Helper, + MachineInstr &MI) const { + assert(MI.getOpcode() == TargetOpcode::G_VASTART && "Unexpected opcode"); + MachineFunction &MF = Helper.MIRBuilder.getMF(); + Z80MachineFunctionInfo &FuncInfo = *MF.getInfo(); + int FrameIdx = FuncInfo.getVarArgsFrameIndex(); + assert(FrameIdx && "Found va_start but never setVarArgsFrameIndex!"); + LLT p0 = LLT::pointer(0, TM.getPointerSizeInBits(0)); + Helper.MIRBuilder.buildStore(Helper.MIRBuilder.buildFrameIndex(p0, FrameIdx), + MI.getOperand(0).getReg(), + **MI.memoperands_begin()); + MI.eraseFromParent(); + return LegalizerHelper::Legalized; +} + +LegalizerHelper::LegalizeResult +Z80LegalizerInfo::legalizeFunnelShift(LegalizerHelper &Helper, + MachineInstr &MI) const { + assert((MI.getOpcode() == TargetOpcode::G_FSHL || + MI.getOpcode() == TargetOpcode::G_FSHR) && + "Unexpected opcode"); + MachineIRBuilder &MIRBuilder = Helper.MIRBuilder; + MachineRegisterInfo &MRI = *MIRBuilder.getMRI(); + Register DstReg = MI.getOperand(0).getReg(); + Register FwdReg = MI.getOperand(1).getReg(); + Register RevReg = MI.getOperand(2).getReg(); + Register AmtReg = MI.getOperand(3).getReg(); + LLT Ty = MRI.getType(DstReg); + + unsigned FwdShiftOpc = TargetOpcode::G_SHL; + unsigned RevShiftOpc = TargetOpcode::G_LSHR; + if (MI.getOpcode() == TargetOpcode::G_FSHR) { + std::swap(FwdReg, RevReg); + std::swap(FwdShiftOpc, RevShiftOpc); + } + + auto MaskI = MIRBuilder.buildConstant(Ty, Ty.getSizeInBits() - 1); + auto FwdAmtI = MIRBuilder.buildAnd(Ty, AmtReg, MaskI); + auto FwdI = MIRBuilder.buildInstr(FwdShiftOpc, {Ty}, {FwdReg, FwdAmtI}); + auto RevAmtI = MIRBuilder.buildAnd( + Ty, MIRBuilder.buildSub(Ty, MIRBuilder.buildConstant(Ty, 0), AmtReg), + MaskI); + auto RevI = MIRBuilder.buildInstr(RevShiftOpc, {Ty}, {RevReg, RevAmtI}); + MIRBuilder.buildOr(DstReg, FwdI, RevI); + MI.eraseFromParent(); + return LegalizerHelper::Legalized; +} + +LegalizerHelper::LegalizeResult +Z80LegalizerInfo::legalizeCompare(LegalizerHelper &Helper, + MachineInstr &MI) const { + MachineIRBuilder &MIRBuilder = Helper.MIRBuilder; + MachineRegisterInfo &MRI = *MIRBuilder.getMRI(); + Register DstReg = MI.getOperand(0).getReg(); + auto Pred = CmpInst::Predicate(MI.getOperand(1).getPredicate()); + Register LHSReg = MI.getOperand(2).getReg(); + Register RHSReg = MI.getOperand(3).getReg(); + LLT OpTy = MRI.getType(LHSReg); + unsigned OpSize = OpTy.getSizeInBits(); + assert(MRI.getType(DstReg) == LLT::scalar(1) && !OpTy.isVector() && + MRI.getType(RHSReg) == OpTy && "Unexpected type"); + + Type *Ty; + RTLIB::Libcall Libcall; + bool IsSigned, IsSwapped, IsConst; + Z80::CondCode CC = + Z80::GetBranchConditionForPredicate(Pred, IsSigned, IsSwapped, IsConst); + if (IsSwapped) + std::swap(LHSReg, RHSReg); + auto &Ctx = MIRBuilder.getMF().getFunction().getContext(); + bool ZeroRHS = false; + if (MI.getOpcode() == TargetOpcode::G_ICMP) { + Ty = IntegerType::get(Ctx, OpSize); + if (auto C = getConstantVRegVal(RHSReg, MRI)) + ZeroRHS = *C == 0; + switch (OpSize) { + case 32: + Libcall = ZeroRHS ? RTLIB::CMP_I32_0 : RTLIB::CMP_I32; + break; + case 64: + Libcall = ZeroRHS ? RTLIB::CMP_I64_0 : RTLIB::CMP_I64; + break; + default: + llvm_unreachable("Unexpected type"); + } + } else { + assert(MI.getOpcode() == TargetOpcode::G_FCMP && "Unexpected opcode"); + assert(OpTy.isScalar() && "Unexpected type"); + switch (OpSize) { + case 32: + Ty = Type::getFloatTy(Ctx); + Libcall = RTLIB::CMP_F32; + break; + case 64: + Ty = Type::getDoubleTy(Ctx); + Libcall = RTLIB::CMP_F64; + break; + default: + llvm_unreachable("Unexpected type"); + } + } + if (!IsConst) { + LLT s8 = LLT::scalar(8); + Type *Int8Ty = Type::getInt8Ty(Ctx); + Register FlagsReg = MRI.createGenericVirtualRegister(s8); + CallLowering::ArgInfo FlagsArg(FlagsReg, Int8Ty); + CallLowering::ArgInfo Args[2] = {{LHSReg, Ty}, {RHSReg, Ty}}; + createLibcall(MIRBuilder, Libcall, FlagsArg, + makeArrayRef(Args, 2 - ZeroRHS)); + if (IsSigned) { + Register SignedFlagsReg = MRI.createGenericVirtualRegister(s8); + CallLowering::ArgInfo SignedFlagsArg(SignedFlagsReg, Int8Ty); + createLibcall(MIRBuilder, RTLIB::SCMP, SignedFlagsArg, FlagsArg); + FlagsReg = SignedFlagsReg; + } + MIRBuilder.buildCopy(Register(Z80::F), FlagsReg); + } else + MIRBuilder.buildInstr(Z80::RCF); + MIRBuilder.buildInstr(Z80::SetCC, {DstReg}, {int64_t(CC)}); + MI.eraseFromParent(); + return LegalizerHelper::Legalized; +} + +bool Z80LegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper, + MachineInstr &MI) const { + MachineIRBuilder &MIRBuilder = Helper.MIRBuilder; + MachineFunction &MF = MIRBuilder.getMF(); + MachineRegisterInfo &MRI = *MIRBuilder.getMRI(); + auto &Ctx = MF.getFunction().getContext(); + auto &CLI = *MF.getSubtarget().getCallLowering(); + bool Is24Bit = Subtarget.is24Bit(); + MIRBuilder.setInstr(MI); + switch (auto IntrinsicID = Intrinsic::ID(MI.getIntrinsicID())) { + case Intrinsic::memcpy: + case Intrinsic::memset: + case Intrinsic::memmove: { + Register DstReg = MI.getOperand(1).getReg(); + LLT DstTy = MRI.getType(DstReg); + Register SrcReg = MI.getOperand(2).getReg(); + Register LenReg = MI.getOperand(3).getReg(); + LLT LenTy = LLT::scalar(Is24Bit ? 24 : 16); + LenReg = MIRBuilder.buildZExtOrTrunc(LenTy, LenReg).getReg(0); + // We need to make sure the number of bytes is non-zero for this lowering to + // be correct. Since we only need to lower constant-length intrinsics for + // now, just support those. + if (auto Len = getConstantVRegVal(LenReg, MRI)) { + // Doing something with zero bytes is a noop anyway. + if (!*Len) + break; + if (MF.getFunction().hasOptSize() && IntrinsicID == Intrinsic::memmove) + // Lowering memmove generates a lot of code... + goto MemLibcall; + if (IntrinsicID == Intrinsic::memset) { + // Store the first byte. + MIRBuilder.buildStore(SrcReg, DstReg, **MI.memoperands_begin()); + // If we are only storing one byte, we are done now. + // TODO: lower small len to a series of stores. + if (*Len == 1) + break; + // Read starting at the stored byte. + SrcReg = DstReg; + // Write starting at the following byte. + auto One = + MIRBuilder.buildConstant(LLT::scalar(DstTy.getSizeInBits()), 1); + DstReg = MIRBuilder.buildPtrAdd(DstTy, DstReg, One).getReg(0); + // Copy one less byte. + auto NegOne = + MIRBuilder.buildConstant(LenTy, -1); + LenReg = MIRBuilder.buildAdd(LenTy, LenReg, NegOne).getReg(0); + // Now it's just an ldir. + } + Register DE = Is24Bit ? Z80::UDE : Z80::DE; + Register HL = Is24Bit ? Z80::UHL : Z80::HL; + Register BC = Is24Bit ? Z80::UBC : Z80::BC; + if (IntrinsicID == Intrinsic::memmove) { + MIRBuilder.buildCopy(HL, SrcReg); + MIRBuilder.buildInstr(Is24Bit ? Z80::CP24ao : Z80::CP16ao, {}, + {DstReg}); + MIRBuilder.buildInstr(Is24Bit ? Z80::LDR24 : Z80::LDR16, {}, + {DstReg, SrcReg, LenReg}).cloneMemRefs(MI); + } else { + // TODO: lower small len to a series of loads and stores. + MIRBuilder.buildCopy(DE, DstReg); + MIRBuilder.buildCopy(HL, SrcReg); + MIRBuilder.buildCopy(BC, LenReg); + MIRBuilder.buildInstr(Is24Bit ? Z80::LDIR24 : Z80::LDIR16) + .cloneMemRefs(MI); + } + break; + } + MemLibcall: + MI.getOperand(3).setReg(LenReg); + if (createMemLibcall(MIRBuilder, MRI, MI) == + LegalizerHelper::UnableToLegalize) + return false; + break; + } + case Intrinsic::trap: { + CallLowering::CallLoweringInfo Info; + Info.CallConv = CallingConv::C; + Info.Callee = MachineOperand::CreateES("abort"); + Info.OrigRet = CallLowering::ArgInfo({0}, Type::getVoidTy(Ctx)); + if (!CLI.lowerCall(MIRBuilder, Info)) + return false; + break; + } + default: + return false; + } + MI.eraseFromParent(); + return true; +} diff --git a/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.h b/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.h new file mode 100644 index 00000000000000..a883eff794c1a9 --- /dev/null +++ b/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.h @@ -0,0 +1,56 @@ +//===- Z80LegalizerInfo.h ----------------------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file declares the targeting of the Machinelegalizer class for Z80. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80MACHINELEGALIZER_H +#define LLVM_LIB_TARGET_Z80_Z80MACHINELEGALIZER_H + +#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" + +namespace llvm { + +class Z80Subtarget; +class Z80TargetMachine; + +/// This class provides the information for the target register banks. +class Z80LegalizerInfo : public LegalizerInfo { +private: + /// Keep a reference to the Z80Subtarget around so that we can + /// make the right decision when generating code for different targets. + const Z80Subtarget &Subtarget; + const Z80TargetMachine &TM; + +public: + Z80LegalizerInfo(const Z80Subtarget &STI, const Z80TargetMachine &TM); + + LegalizerHelper::LegalizeResult + legalizeCustomMaybeLegal(LegalizerHelper &Helper, + MachineInstr &MI) const override; + + bool legalizeIntrinsic(LegalizerHelper &Helper, + MachineInstr &MI) const override; + +private: + LegalizerHelper::LegalizeResult legalizeBitwise(LegalizerHelper &Helper, + MachineInstr &MI) const; + LegalizerHelper::LegalizeResult legalizeFConstant(LegalizerHelper &Helper, + MachineInstr &MI) const; + LegalizerHelper::LegalizeResult legalizeVAStart(LegalizerHelper &Helper, + MachineInstr &MI) const; + LegalizerHelper::LegalizeResult legalizeFunnelShift(LegalizerHelper &Helper, + MachineInstr &MI) const; + LegalizerHelper::LegalizeResult legalizeCompare(LegalizerHelper &Helper, + MachineInstr &MI) const; +}; + +} // End namespace llvm + +#endif diff --git a/llvm/lib/Target/Z80/GISel/Z80RegisterBankInfo.cpp b/llvm/lib/Target/Z80/GISel/Z80RegisterBankInfo.cpp new file mode 100644 index 00000000000000..cd20bd7c16fba1 --- /dev/null +++ b/llvm/lib/Target/Z80/GISel/Z80RegisterBankInfo.cpp @@ -0,0 +1,169 @@ +//===- Z80RegisterBankInfo.cpp -----------------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the targeting of the RegisterBankInfo class for Z80. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#include "Z80RegisterBankInfo.h" +#include "Z80RegisterInfo.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "llvm/CodeGen/GlobalISel/RegisterBank.h" +#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" + +#define GET_TARGET_REGBANK_IMPL +#include "Z80GenRegisterBank.inc" + +using namespace llvm; +// This file will be TableGen'ed at some point. +#define GET_TARGET_REGBANK_INFO_IMPL +#include "Z80GenRegisterBankInfo.def" + +Z80RegisterBankInfo::Z80RegisterBankInfo(const TargetRegisterInfo &TRI) { + + // validate RegBank initialization. + const RegisterBank &RBGPR = getRegBank(Z80::GPRRegBankID); + (void)RBGPR; + assert(&Z80::GPRRegBank == &RBGPR && "Incorrect RegBanks initialization."); + + // The GPR register bank is fully defined by all the registers in + // R24 + its subclasses. + assert(RBGPR.covers(*TRI.getRegClass(Z80::R24RegClassID)) && + "Subclass not added?"); + assert(RBGPR.getSize() == 24 && "GPRs should hold up to 24-bits"); +} + +const RegisterBank & +Z80RegisterBankInfo::getRegBankFromRegClass(const TargetRegisterClass &RC, + LLT) const { + if (Z80::R8RegClass.hasSubClassEq(&RC) || + Z80::R16RegClass.hasSubClassEq(&RC) || + Z80::R24RegClass.hasSubClassEq(&RC) || + Z80::F8RegClass.hasSubClassEq(&RC) || + Z80::Z16RegClass.hasSubClassEq(&RC) || + Z80::Z24RegClass.hasSubClassEq(&RC)) + return getRegBank(Z80::GPRRegBankID); + + llvm_unreachable("Unsupported register kind."); +} + +Z80GenRegisterBankInfo::PartialMappingIdx +Z80GenRegisterBankInfo::getPartialMappingIdx(const LLT &Ty) { + if (Ty.isVector()) + llvm_unreachable("Vector is unsupported."); + + switch (Ty.getSizeInBits()) { + case 1: + case 8: return PMI_GPR8; + case 16: return PMI_GPR16; + case 24: return PMI_GPR24; + default: + llvm_unreachable("Unsupported register size."); + } +} + +void Z80RegisterBankInfo::getInstrPartialMappingIdxs( + const MachineInstr &MI, const MachineRegisterInfo &MRI, + SmallVectorImpl &OpRegBankIdx) { + + unsigned NumOperands = MI.getNumOperands(); + for (unsigned Idx = 0; Idx < NumOperands; ++Idx) { + auto &MO = MI.getOperand(Idx); + if (!MO.isReg()) + OpRegBankIdx[Idx] = PMI_None; + else + OpRegBankIdx[Idx] = getPartialMappingIdx(MRI.getType(MO.getReg())); + } +} + +bool Z80RegisterBankInfo::getInstrValueMapping( + const MachineInstr &MI, + const SmallVectorImpl &OpRegBankIdx, + SmallVectorImpl &OpdsMapping) { + + unsigned NumOperands = MI.getNumOperands(); + for (unsigned Idx = 0; Idx < NumOperands; ++Idx) { + if (!MI.getOperand(Idx).isReg()) + continue; + + auto Mapping = getValueMapping(OpRegBankIdx[Idx], 1); + if (!Mapping->isValid()) + return false; + + OpdsMapping[Idx] = Mapping; + } + return true; +} + +const RegisterBankInfo::InstructionMapping & +Z80RegisterBankInfo::getSameOperandsMapping(const MachineInstr &MI) const { + const MachineFunction &MF = *MI.getParent()->getParent(); + const MachineRegisterInfo &MRI = MF.getRegInfo(); + + unsigned NumOperands = MI.getNumOperands(); + LLT Ty = MRI.getType(MI.getOperand(0).getReg()); + + if (NumOperands != 3 || (Ty != MRI.getType(MI.getOperand(1).getReg())) || + (Ty != MRI.getType(MI.getOperand(2).getReg()))) + llvm_unreachable("Unsupported operand mapping yet."); + + auto Mapping = getValueMapping(getPartialMappingIdx(Ty), 3); + return getInstructionMapping(DefaultMappingID, 1, Mapping, NumOperands); +} + +const RegisterBankInfo::InstructionMapping & +Z80RegisterBankInfo::getInstrMapping(const MachineInstr &MI) const { + const MachineFunction &MF = *MI.getParent()->getParent(); + const MachineRegisterInfo &MRI = MF.getRegInfo(); + unsigned Opc = MI.getOpcode(); + unsigned NumOperands = MI.getNumOperands(); + + // Try the default logic for non-generic instructions that are either copies + // or already have some operands assigned to banks. + if (!isPreISelGenericOpcode(Opc) || Opc == TargetOpcode::G_PHI) { + const InstructionMapping &Mapping = getInstrMappingImpl(MI); + if (Mapping.isValid()) + return Mapping; + } + + switch (Opc) { + case TargetOpcode::G_ADD: + case TargetOpcode::G_SUB: + case TargetOpcode::G_MUL: + return getSameOperandsMapping(MI); + case TargetOpcode::G_SHL: + case TargetOpcode::G_LSHR: + case TargetOpcode::G_ASHR: { + LLT Ty = MRI.getType(MI.getOperand(0).getReg()); + + auto Mapping = getValueMapping(getPartialMappingIdx(Ty), 3); + return getInstructionMapping(DefaultMappingID, 1, Mapping, NumOperands); + } + default: + break; + } + + // Track the bank of each register. + SmallVector OpRegBankIdx(NumOperands); + getInstrPartialMappingIdxs(MI, MRI, OpRegBankIdx); + + // Finally construct the computed mapping. + SmallVector OpdsMapping(NumOperands); + if (!getInstrValueMapping(MI, OpRegBankIdx, OpdsMapping)) + return getInvalidInstructionMapping(); + + return getInstructionMapping(DefaultMappingID, /* Cost */ 1, + getOperandsMapping(OpdsMapping), NumOperands); +} + +void Z80RegisterBankInfo::applyMappingImpl( + const OperandsMapper &OpdMapper) const { + return applyDefaultMapping(OpdMapper); +} diff --git a/llvm/lib/Target/Z80/GISel/Z80RegisterBankInfo.h b/llvm/lib/Target/Z80/GISel/Z80RegisterBankInfo.h new file mode 100644 index 00000000000000..7b9e2b9b52e1a0 --- /dev/null +++ b/llvm/lib/Target/Z80/GISel/Z80RegisterBankInfo.h @@ -0,0 +1,79 @@ +//===- Z80RegisterBankInfo ---------------------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file declares the targeting of the RegisterBankInfo class for Z80. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80REGISTERBANKINFO_H +#define LLVM_LIB_TARGET_Z80_Z80REGISTERBANKINFO_H + +#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" + +#define GET_REGBANK_DECLARATIONS +#include "Z80GenRegisterBank.inc" + +namespace llvm { + +class LLT; + +class Z80GenRegisterBankInfo : public RegisterBankInfo { +protected: +#define GET_TARGET_REGBANK_CLASS +#include "Z80GenRegisterBank.inc" +#define GET_TARGET_REGBANK_INFO_CLASS +#include "Z80GenRegisterBankInfo.def" + + static RegisterBankInfo::PartialMapping PartMappings[]; + static RegisterBankInfo::ValueMapping ValMappings[]; + + static PartialMappingIdx getPartialMappingIdx(const LLT &Ty); + static const RegisterBankInfo::ValueMapping * + getValueMapping(PartialMappingIdx Idx, unsigned NumOperands); +}; + +class TargetRegisterInfo; + +/// This class provides the information for the target register banks. +class Z80RegisterBankInfo final : public Z80GenRegisterBankInfo { +private: + /// Get an instruction mapping. + /// \return An InstructionMappings with a statically allocated + /// OperandsMapping. + const InstructionMapping & + getSameOperandsMapping(const MachineInstr &MI) const; + + /// Track the bank of each instruction operand(register) + static void + getInstrPartialMappingIdxs(const MachineInstr &MI, + const MachineRegisterInfo &MRI, + SmallVectorImpl &OpRegBankIdx); + + /// Construct the instruction ValueMapping from PartialMappingIdxs + /// \return true if mapping succeeded. + static bool + getInstrValueMapping(const MachineInstr &MI, + const SmallVectorImpl &OpRegBankIdx, + SmallVectorImpl &OpdsMapping); + +public: + Z80RegisterBankInfo(const TargetRegisterInfo &TRI); + + const RegisterBank &getRegBankFromRegClass(const TargetRegisterClass &RC, + LLT Ty) const override; + + /// See RegisterBankInfo::applyMapping. + void applyMappingImpl(const OperandsMapper &OpdMapper) const override; + + const InstructionMapping & + getInstrMapping(const MachineInstr &MI) const override; +}; + +} // End namespace llvm + +#endif diff --git a/llvm/lib/Target/Z80/Z80.h b/llvm/lib/Target/Z80/Z80.h index f2d731f2fc334e..9d1cd16c41fb16 100644 --- a/llvm/lib/Target/Z80/Z80.h +++ b/llvm/lib/Target/Z80/Z80.h @@ -15,11 +15,21 @@ #ifndef LLVM_LIB_TARGET_Z80_Z80_H #define LLVM_LIB_TARGET_Z80_Z80_H -#include "llvm/Support/CodeGen.h" - namespace llvm { class FunctionPass; +class InstructionSelector; +class PassRegistry; +class Z80RegisterBankInfo; +class Z80Subtarget; +class Z80TargetMachine; + +InstructionSelector *createZ80InstructionSelector(const Z80TargetMachine &TM, + Z80Subtarget &, + Z80RegisterBankInfo &); + +void initializeZ80PreLegalizerCombinerPass(PassRegistry &); +void initializeZ80PostSelectCombinerPass(PassRegistry &); } // namespace llvm diff --git a/llvm/lib/Target/Z80/Z80.td b/llvm/lib/Target/Z80/Z80.td index 666ee2416057f5..f35763c41761c8 100644 --- a/llvm/lib/Target/Z80/Z80.td +++ b/llvm/lib/Target/Z80/Z80.td @@ -55,6 +55,7 @@ def : Proc<"ez80", [FeatureZ180, FeatureEZ80, FeatureIdxHalf]>; //===----------------------------------------------------------------------===// include "Z80RegisterInfo.td" +include "Z80RegisterBanks.td" //===----------------------------------------------------------------------===// // Instruction Descriptions diff --git a/llvm/lib/Target/Z80/Z80GenRegisterBankInfo.def b/llvm/lib/Target/Z80/Z80GenRegisterBankInfo.def new file mode 100644 index 00000000000000..bfb17dec8eb7b6 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80GenRegisterBankInfo.def @@ -0,0 +1,72 @@ +//===- Z80GenRegisterBankInfo.def --------------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file defines all the static objects used by Z80RegisterBankInfo. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#ifdef GET_TARGET_REGBANK_INFO_IMPL +RegisterBankInfo::PartialMapping Z80GenRegisterBankInfo::PartMappings[]{ + /* StartIdx, Length, RegBank */ + // GPR value + {0, 8, Z80::GPRRegBank}, // :0 + {0, 16, Z80::GPRRegBank}, // :1 + {0, 24, Z80::GPRRegBank}, // :2 +}; +#endif // GET_TARGET_REGBANK_INFO_IMPL + +#ifdef GET_TARGET_REGBANK_INFO_CLASS +enum PartialMappingIdx { + PMI_None = -1, + PMI_GPR8, + PMI_GPR16, + PMI_GPR24 +}; +#endif // GET_TARGET_REGBANK_INFO_CLASS + +#ifdef GET_TARGET_REGBANK_INFO_IMPL +#define INSTR_3OP(INFO) INFO, INFO, INFO, +#define BREAKDOWN(INDEX, NUM) \ + { &Z80GenRegisterBankInfo::PartMappings[INDEX], NUM } +// ValueMappings. +RegisterBankInfo::ValueMapping Z80GenRegisterBankInfo::ValMappings[]{ + /* BreakDown, NumBreakDowns */ + // 3-operands instructions (all binary operations should end up with one of + // those mapping). + INSTR_3OP(BREAKDOWN(PMI_GPR8, 1)) // 0: GPR_8 + INSTR_3OP(BREAKDOWN(PMI_GPR16, 1)) // 3: GPR_16 + INSTR_3OP(BREAKDOWN(PMI_GPR24, 1)) // 6: GPR_32 +}; +#undef INSTR_3OP +#undef BREAKDOWN +#endif // GET_TARGET_REGBANK_INFO_IMPL + +#ifdef GET_TARGET_REGBANK_INFO_CLASS +enum ValueMappingIdx { + VMI_None = -1, + VMI_3OpsGpr8Idx = PMI_GPR8 * 3, + VMI_3OpsGpr16Idx = PMI_GPR16 * 3, + VMI_3OpsGpr24Idx = PMI_GPR24 * 3, +}; +#undef GET_TARGET_REGBANK_INFO_CLASS +#endif // GET_TARGET_REGBANK_INFO_CLASS + +#ifdef GET_TARGET_REGBANK_INFO_IMPL +#undef GET_TARGET_REGBANK_INFO_IMPL +const RegisterBankInfo::ValueMapping * +Z80GenRegisterBankInfo::getValueMapping(PartialMappingIdx Idx, + unsigned NumOperands) { + // We can use VMI_3Ops Mapping for all the cases. + if (NumOperands <= 3 && (Idx >= PMI_GPR8 && Idx <= PMI_GPR24)) + return &ValMappings[(unsigned)Idx * 3]; + + llvm_unreachable("Unsupported PartialMappingIdx."); +} + +#endif // GET_TARGET_REGBANK_INFO_IMPL + diff --git a/llvm/lib/Target/Z80/Z80ISelLowering.cpp b/llvm/lib/Target/Z80/Z80ISelLowering.cpp index 7d844a4f5349e6..05263a60e08609 100644 --- a/llvm/lib/Target/Z80/Z80ISelLowering.cpp +++ b/llvm/lib/Target/Z80/Z80ISelLowering.cpp @@ -265,6 +265,7 @@ Z80TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, case Z80::Cp16ao: case Z80::Cp24ao: return EmitLoweredCmp(MI, BB);*/ + case Z80::SetCC: case Z80::Select8: case Z80::Select16: case Z80::Select24: diff --git a/llvm/lib/Target/Z80/Z80RegisterBanks.td b/llvm/lib/Target/Z80/Z80RegisterBanks.td new file mode 100644 index 00000000000000..e94ca167ca8170 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80RegisterBanks.td @@ -0,0 +1,13 @@ +//=- Z80RegisterBank.td - Describe the AArch64 Banks -----*- tablegen -*-=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +/// General Purpose Registers +def GPRRegBank : RegisterBank<"GPR", [R24, Z24, Z16, F8]>; diff --git a/llvm/lib/Target/Z80/Z80Subtarget.cpp b/llvm/lib/Target/Z80/Z80Subtarget.cpp index 62420fb7505f71..b49d8c1931209e 100644 --- a/llvm/lib/Target/Z80/Z80Subtarget.cpp +++ b/llvm/lib/Target/Z80/Z80Subtarget.cpp @@ -12,9 +12,13 @@ //===----------------------------------------------------------------------===// #include "Z80Subtarget.h" +#include "GISel/Z80CallLowering.h" +#include "GISel/Z80LegalizerInfo.h" +#include "GISel/Z80RegisterBankInfo.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" #include "Z80.h" #include "Z80TargetMachine.h" -#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelect.h" using namespace llvm; #define DEBUG_TYPE "z80-subtarget" @@ -38,4 +42,11 @@ Z80Subtarget::Z80Subtarget(const Triple &TT, StringRef CPU, StringRef FS, In16BitMode(TT.isArch16Bit() || TT.getEnvironment() == Triple::CODE16), In24BitMode(!In16BitMode), InstrInfo(initializeSubtargetDependencies(CPU, FS)), TLInfo(TM, *this), - FrameLowering(*this) {} + FrameLowering(*this) { + CallLoweringInfo.reset(new Z80CallLowering(*getTargetLowering())); + Legalizer.reset(new Z80LegalizerInfo(*this, TM)); + + auto *RBI = new Z80RegisterBankInfo(*getRegisterInfo()); + RegBankInfo.reset(RBI); + InstSelector.reset(createZ80InstructionSelector(TM, *this, *RBI)); +} diff --git a/llvm/lib/Target/Z80/Z80Subtarget.h b/llvm/lib/Target/Z80/Z80Subtarget.h index 18b878fad7ac8a..d171fe4d6b37ed 100644 --- a/llvm/lib/Target/Z80/Z80Subtarget.h +++ b/llvm/lib/Target/Z80/Z80Subtarget.h @@ -17,6 +17,10 @@ #include "Z80FrameLowering.h" #include "Z80InstrInfo.h" #include "Z80ISelLowering.h" +#include "llvm/CodeGen/GlobalISel/CallLowering.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelector.h" +#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" +#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #define GET_SUBTARGETINFO_HEADER @@ -53,6 +57,12 @@ class Z80Subtarget final : public Z80GenSubtargetInfo { Z80TargetLowering TLInfo; Z80FrameLowering FrameLowering; + /// GlobalISel related APIs. + std::unique_ptr CallLoweringInfo; + std::unique_ptr Legalizer; + std::unique_ptr RegBankInfo; + std::unique_ptr InstSelector; + public: /// This constructor initializes the data members to match that /// of the specified triple. @@ -74,6 +84,20 @@ class Z80Subtarget final : public Z80GenSubtargetInfo { /// subtarget options. Definition of function is auto generated by tblgen. void ParseSubtargetFeatures(StringRef CPU, StringRef FS); + /// Methods used by Global ISel + const CallLowering *getCallLowering() const override { + return CallLoweringInfo.get(); + } + InstructionSelector *getInstructionSelector() const override { + return InstSelector.get(); + } + const LegalizerInfo *getLegalizerInfo() const override { + return Legalizer.get(); + } + const RegisterBankInfo *getRegBankInfo() const override { + return RegBankInfo.get(); + } + private: Z80Subtarget &initializeSubtargetDependencies(StringRef CPU, StringRef FS); void initializeEnvironment(); diff --git a/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp b/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp index 2cfab39d456228..caded0b95b3033 100644 --- a/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp +++ b/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp @@ -42,7 +42,7 @@ TEST_F(AArch64GISelMITest, LowerBitCountingCTTZ0) { LegalizerHelper Helper(*MF, Info, Observer, B); // Perform Legalization EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*MIBCTTZ, 0, LLT::scalar(64))); + Helper.lower(*MIBCTTZ, 0)); auto CheckStr = R"( CHECK: [[CZU:%[0-9]+]]:_(s32) = G_CTTZ_ZERO_UNDEF %0 @@ -73,8 +73,9 @@ TEST_F(AArch64GISelMITest, LowerBitCountingCTTZ1) { DummyGISelObserver Observer; LegalizerHelper Helper(*MF, Info, Observer, B); // Perform Legalization - EXPECT_TRUE(Helper.lower(*MIBCTTZ, 0, LLT::scalar(64)) == - LegalizerHelper::LegalizeResult::Legalized); + + EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, + Helper.lower(*MIBCTTZ, 0)); auto CheckStr = R"( CHECK: [[NEG1:%[0-9]+]]:_(s64) = G_CONSTANT i64 -1 @@ -178,8 +179,8 @@ TEST_F(AArch64GISelMITest, LowerBitCountingCTTZ2) { LegalizerHelper Helper(*MF, Info, Observer, B); B.setInsertPt(*EntryMBB, MIBCTTZ->getIterator()); - EXPECT_TRUE(Helper.lower(*MIBCTTZ, 0, LLT::scalar(64)) == - LegalizerHelper::LegalizeResult::Legalized); + EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, + Helper.lower(*MIBCTTZ, 0)); auto CheckStr = R"( CHECK: [[NEG1:%[0-9]+]]:_(s64) = G_CONSTANT i64 -1 @@ -274,8 +275,8 @@ TEST_F(AArch64GISelMITest, LowerBitCountingCTTZ3) { AInfo Info(MF->getSubtarget()); DummyGISelObserver Observer; LegalizerHelper Helper(*MF, Info, Observer, B); - EXPECT_TRUE(Helper.lower(*MIBCTTZ, 0, LLT::scalar(64)) == - LegalizerHelper::LegalizeResult::Legalized); + EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, + Helper.lower(*MIBCTTZ, 0)); auto CheckStr = R"( CHECK: CTTZ @@ -301,8 +302,8 @@ TEST_F(AArch64GISelMITest, LowerBitCountingCTLZ0) { AInfo Info(MF->getSubtarget()); DummyGISelObserver Observer; LegalizerHelper Helper(*MF, Info, Observer, B); - EXPECT_TRUE(Helper.lower(*MIBCTLZ, 0, LLT::scalar(64)) == - LegalizerHelper::LegalizeResult::Legalized); + EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, + Helper.lower(*MIBCTLZ, 0)); auto CheckStr = R"( CHECK: [[CZU:%[0-9]+]]:_(s64) = G_CTLZ_ZERO_UNDEF %0 @@ -333,7 +334,7 @@ TEST_F(AArch64GISelMITest, LowerBitCountingCTLZLibcall) { DummyGISelObserver Observer; LegalizerHelper Helper(*MF, Info, Observer, B); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*MIBCTLZ, 0, LLT::scalar(32))); + Helper.lower(*MIBCTLZ, 0)); auto CheckStr = R"( CHECK: [[CZU:%[0-9]+]]:_(s32) = G_CTLZ_ZERO_UNDEF %0 @@ -365,8 +366,8 @@ TEST_F(AArch64GISelMITest, LowerBitCountingCTLZ1) { AInfo Info(MF->getSubtarget()); DummyGISelObserver Observer; LegalizerHelper Helper(*MF, Info, Observer, B); - EXPECT_TRUE(Helper.lower(*MIBCTLZ, 0, s8) == - LegalizerHelper::LegalizeResult::Legalized); + EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, + Helper.lower(*MIBCTLZ, 0)); auto CheckStr = R"( CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNC @@ -850,9 +851,9 @@ TEST_F(AArch64GISelMITest, LowerFNEG) { LegalizerHelper Helper(*MF, Info, Observer, B); // Perform Legalization EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*FNeg0, 0, LLT::scalar(64))); + Helper.lower(*FNeg0, 0)); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*FNeg1, 0, LLT::scalar(64))); + Helper.lower(*FNeg1, 0)); auto CheckStr = R"( CHECK: [[FADD:%[0-9]+]]:_(s64) = nsz G_FADD %0:_, %1:_ @@ -895,22 +896,22 @@ TEST_F(AArch64GISelMITest, LowerMinMax) { DummyGISelObserver Observer; LegalizerHelper Helper(*MF, Info, Observer, B); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*SMin, 0, s64)); + Helper.lower(*SMin, 0)); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*SMax, 0, s64)); + Helper.lower(*SMax, 0)); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*UMin, 0, s64)); + Helper.lower(*UMin, 0)); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*UMax, 0, s64)); + Helper.lower(*UMax, 0)); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*SMinV, 0, v2s32)); + Helper.lower(*SMinV, 0)); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*SMaxV, 0, v2s32)); + Helper.lower(*SMaxV, 0)); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*UMinV, 0, v2s32)); + Helper.lower(*UMinV, 0)); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*UMaxV, 0, v2s32)); + Helper.lower(*UMaxV, 0)); auto CheckStr = R"( CHECK: [[CMP0:%[0-9]+]]:_(s1) = G_ICMP intpred(slt), %0:_(s64), %1:_ @@ -1238,8 +1239,7 @@ TEST_F(AArch64GISelMITest, LowerSEXTINREG) { DummyGISelObserver Observer; LegalizerHelper Helper(*MF, Info, Observer, B); // Perform Legalization - ASSERT_TRUE(Helper.lower(*MIB, 0, LLT()) == - LegalizerHelper::LegalizeResult::Legalized); + ASSERT_EQ(LegalizerHelper::LegalizeResult::Legalized, Helper.lower(*MIB, 0)); auto CheckStr = R"( CHECK: [[T1:%[0-9]+]]:_(s32) = G_TRUNC From 2ed69778364ebe38a81f7b2bbdf7170514ba0a73 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 27 Oct 2019 17:25:40 +0100 Subject: [PATCH 14/21] [Z80] Add Z80 support to clang. --- clang/include/clang/AST/ASTContext.h | 17 ++- clang/include/clang/AST/BuiltinTypes.def | 6 ++ clang/include/clang/AST/Type.h | 15 ++- clang/include/clang/AST/TypeProperties.td | 7 +- clang/include/clang/Basic/Attr.td | 33 ++++-- clang/include/clang/Basic/AttrDocs.td | 9 ++ .../clang/Basic/DiagnosticSemaKinds.td | 3 + clang/include/clang/Basic/Specifiers.h | 1 + clang/include/clang/Basic/TargetInfo.h | 17 +-- clang/include/clang/Basic/TokenKinds.def | 1 + clang/include/clang/CodeGen/CGFunctionInfo.h | 10 +- clang/include/clang/Driver/Options.td | 2 + clang/include/clang/Sema/DeclSpec.h | 1 + .../include/clang/Serialization/ASTBitCodes.h | 13 +++ clang/lib/AST/ASTContext.cpp | 54 ++++++++-- clang/lib/AST/ASTStructuralEquivalence.cpp | 2 + clang/lib/AST/ExprConstant.cpp | 1 + clang/lib/AST/ItaniumMangle.cpp | 6 ++ clang/lib/AST/MicrosoftMangle.cpp | 4 +- clang/lib/AST/NSAPI.cpp | 2 + clang/lib/AST/PrintfFormatString.cpp | 2 + clang/lib/AST/Type.cpp | 4 + clang/lib/AST/TypeLoc.cpp | 2 + clang/lib/AST/TypePrinter.cpp | 3 + clang/lib/Basic/CMakeLists.txt | 1 + clang/lib/Basic/TargetInfo.cpp | 1 + clang/lib/Basic/Targets.cpp | 6 ++ clang/lib/Basic/Targets/Z80.cpp | 60 +++++++++++ clang/lib/Basic/Targets/Z80.h | 102 ++++++++++++++++++ clang/lib/CodeGen/CGCall.cpp | 3 + clang/lib/CodeGen/CGDebugInfo.cpp | 2 + clang/lib/CodeGen/CodeGenTypes.cpp | 5 + clang/lib/CodeGen/ItaniumCXXABI.cpp | 2 + clang/lib/CodeGen/TargetInfo.cpp | 82 ++++++++++++++ clang/lib/Driver/CMakeLists.txt | 1 + clang/lib/Driver/ToolChains/Arch/Z80.cpp | 49 +++++++++ clang/lib/Driver/ToolChains/Arch/Z80.h | 36 +++++++ clang/lib/Driver/ToolChains/Clang.cpp | 2 + clang/lib/Driver/ToolChains/CommonArgs.cpp | 5 + clang/lib/Format/FormatToken.cpp | 1 + clang/lib/Frontend/InitPreprocessor.cpp | 23 ++++ clang/lib/Index/USRGeneration.cpp | 2 + clang/lib/Parse/ParseDecl.cpp | 7 ++ clang/lib/Sema/DeclSpec.cpp | 9 +- clang/lib/Sema/Sema.cpp | 13 +++ clang/lib/Sema/SemaDecl.cpp | 1 + clang/lib/Sema/SemaDeclAttr.cpp | 20 ++++ clang/lib/Sema/SemaTemplateVariadic.cpp | 1 + clang/lib/Sema/SemaType.cpp | 24 +++++ clang/lib/Serialization/ASTCommon.cpp | 6 ++ clang/lib/Serialization/ASTReader.cpp | 12 +++ clang/lib/Serialization/ASTWriter.cpp | 3 + .../TypeSystem/Clang/TypeSystemClang.cpp | 2 + .../llvm/CodeGen/GlobalISel/CallLowering.h | 4 + llvm/include/llvm/IR/CallingConv.h | 1 + llvm/include/llvm/MC/MCSectionOMF.h | 8 +- llvm/lib/CodeGen/GlobalISel/CallLowering.cpp | 1 + llvm/lib/MC/MCSectionOMF.cpp | 2 +- llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp | 15 +++ llvm/lib/Target/Z80/Z80CallingConv.td | 2 + llvm/lib/Target/Z80/Z80RegisterInfo.cpp | 4 + 61 files changed, 692 insertions(+), 41 deletions(-) create mode 100644 clang/lib/Basic/Targets/Z80.cpp create mode 100644 clang/lib/Basic/Targets/Z80.h create mode 100644 clang/lib/Driver/ToolChains/Arch/Z80.cpp create mode 100644 clang/lib/Driver/ToolChains/Arch/Z80.h diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 2b988be60da9f4..0322cdc0631d87 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -312,6 +312,12 @@ class ASTContext : public RefCountedBase { TemplateTemplateParmDecl * getCanonicalTemplateTemplateParmDecl(TemplateTemplateParmDecl *TTP) const; + /// The typedef for the __int48_t type. + mutable TypedefDecl *Int48Decl = nullptr; + + /// The typedef for the __uint48_t type. + mutable TypedefDecl *UInt48Decl = nullptr; + /// The typedef for the __int128_t type. mutable TypedefDecl *Int128Decl = nullptr; @@ -948,9 +954,10 @@ class ASTContext : public RefCountedBase { CanQualType Char8Ty; // [C++20 proposal] CanQualType Char16Ty; // [C++0x 3.9.1p5], integer type in C99. CanQualType Char32Ty; // [C++0x 3.9.1p5], integer type in C99. - CanQualType SignedCharTy, ShortTy, IntTy, LongTy, LongLongTy, Int128Ty; + CanQualType SignedCharTy, ShortTy, IntTy, LongTy, Int48Ty, LongLongTy, + Int128Ty; CanQualType UnsignedCharTy, UnsignedShortTy, UnsignedIntTy, UnsignedLongTy; - CanQualType UnsignedLongLongTy, UnsignedInt128Ty; + CanQualType UnsignedInt48Ty, UnsignedLongLongTy, UnsignedInt128Ty; CanQualType FloatTy, DoubleTy, LongDoubleTy, Float128Ty; CanQualType ShortAccumTy, AccumTy, LongAccumTy; // ISO/IEC JTC1 SC22 WG14 N1169 Extension @@ -1045,6 +1052,12 @@ class ASTContext : public RefCountedBase { /// Create a new implicit TU-level typedef declaration. TypedefDecl *buildImplicitTypedef(QualType T, StringRef Name) const; + /// Retrieve the declaration for the 48-bit signed integer type. + TypedefDecl *getInt48Decl() const; + + /// Retrieve the declaration for the 48-bit unsigned integer type. + TypedefDecl *getUInt48Decl() const; + /// Retrieve the declaration for the 128-bit signed integer type. TypedefDecl *getInt128Decl() const; diff --git a/clang/include/clang/AST/BuiltinTypes.def b/clang/include/clang/AST/BuiltinTypes.def index 039765dfdfea67..dccd8bbc5f1c96 100644 --- a/clang/include/clang/AST/BuiltinTypes.def +++ b/clang/include/clang/AST/BuiltinTypes.def @@ -89,6 +89,9 @@ UNSIGNED_TYPE(UInt, UnsignedIntTy) // 'unsigned long' UNSIGNED_TYPE(ULong, UnsignedLongTy) +// '__uint48_t' +UNSIGNED_TYPE(UInt48, UnsignedInt48Ty) + // 'unsigned long long' UNSIGNED_TYPE(ULongLong, UnsignedLongLongTy) @@ -115,6 +118,9 @@ SIGNED_TYPE(Int, IntTy) // 'long' or 'signed long' SIGNED_TYPE(Long, LongTy) +// '__int48_t' +SIGNED_TYPE(Int48, Int48Ty) + // 'long long' or 'signed long long' SIGNED_TYPE(LongLong, LongLongTy) diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 10b8b41efeeb0a..92bc01f859c7a0 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -3664,6 +3664,8 @@ class FunctionType : public Type { // | CC |noreturn|produces|nocallersavedregs|regparm|nocfcheck|cmsenscall| // |0 .. 4| 5 | 6 | 7 |8 .. 10| 11 | 12 | + // |tiflags| + // | 13 | // // regparm is either 0 (no regparm attribute) or the regparm value+1. enum { CallConvMask = 0x1F }; @@ -3676,6 +3678,7 @@ class FunctionType : public Type { }; enum { NoCfCheckMask = 0x800 }; enum { CmseNSCallMask = 0x1000 }; + enum { TIFlagsMask = 0x2000 }; uint16_t Bits = CC_C; ExtInfo(unsigned Bits) : Bits(static_cast(Bits)) {} @@ -3685,14 +3688,14 @@ class FunctionType : public Type { // have all the elements (when reading an AST file for example). ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc, bool producesResult, bool noCallerSavedRegs, bool NoCfCheck, - bool cmseNSCall) { + bool cmseNSCall, bool tiFlags) { assert((!hasRegParm || regParm < 7) && "Invalid regparm value"); Bits = ((unsigned)cc) | (noReturn ? NoReturnMask : 0) | (producesResult ? ProducesResultMask : 0) | (noCallerSavedRegs ? NoCallerSavedRegsMask : 0) | (hasRegParm ? ((regParm + 1) << RegParmOffset) : 0) | (NoCfCheck ? NoCfCheckMask : 0) | - (cmseNSCall ? CmseNSCallMask : 0); + (cmseNSCall ? CmseNSCallMask : 0) | (tiFlags ? TIFlagsMask : 0); } // Constructor with all defaults. Use when for example creating a @@ -3708,6 +3711,7 @@ class FunctionType : public Type { bool getCmseNSCall() const { return Bits & CmseNSCallMask; } bool getNoCallerSavedRegs() const { return Bits & NoCallerSavedRegsMask; } bool getNoCfCheck() const { return Bits & NoCfCheckMask; } + bool getTIFlags() const { return Bits & TIFlagsMask; } bool getHasRegParm() const { return ((Bits & RegParmMask) >> RegParmOffset) != 0; } unsigned getRegParm() const { @@ -3764,6 +3768,13 @@ class FunctionType : public Type { return ExtInfo(Bits & ~NoCfCheckMask); } + ExtInfo withTIFlags(bool tiFlags) const { + if (tiFlags) + return ExtInfo(Bits | TIFlagsMask); + else + return ExtInfo(Bits & ~TIFlagsMask); + } + ExtInfo withRegParm(unsigned RegParm) const { assert(RegParm < 7 && "Invalid regparm value"); return ExtInfo((Bits & ~RegParmMask) | diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index 4540ea0e1952aa..fce7ccfe351d23 100644 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -287,6 +287,9 @@ let Class = FunctionType in { def : Property<"cmseNSCall", Bool> { let Read = [{ node->getExtInfo().getCmseNSCall() }]; } + def : Property<"tiFlags", Bool> { + let Read = [{ node->getExtInfo().getTIFlags() }]; + } } let Class = FunctionNoProtoType in { @@ -294,7 +297,7 @@ let Class = FunctionNoProtoType in { auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm, callingConvention, producesResult, noCallerSavedRegs, noCfCheck, - cmseNSCall); + cmseNSCall, tiFlags); return ctx.getFunctionNoProtoType(returnType, extInfo); }]>; } @@ -328,7 +331,7 @@ let Class = FunctionProtoType in { auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm, callingConvention, producesResult, noCallerSavedRegs, noCfCheck, - cmseNSCall); + cmseNSCall, tiFlags); FunctionProtoType::ExtProtoInfo epi; epi.ExtInfo = extInfo; epi.Variadic = variadic; diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index bc4a380545afe5..7d1fd2f2392d7e 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -365,6 +365,7 @@ def TargetMSP430 : TargetArch<["msp430"]>; def TargetRISCV : TargetArch<["riscv32", "riscv64"]>; def TargetX86 : TargetArch<["x86"]>; def TargetAnyX86 : TargetArch<["x86", "x86_64"]>; +def TargetAnyZ80 : TargetArch<["z80", "ez80"]>; def TargetWebAssembly : TargetArch<["wasm32", "wasm64"]>; def TargetWindows : TargetArch<["x86", "x86_64", "arm", "thumb", "aarch64"]> { let OSes = ["Win32"]; @@ -743,8 +744,8 @@ def Annotate : InheritableParamAttr { } def ARMInterrupt : InheritableAttr, TargetSpecificAttr { - // NOTE: If you add any additional spellings, MSP430Interrupt's, - // MipsInterrupt's and AnyX86Interrupt's spellings must match. + // NOTE: If you add any additional spellings, ARMInterrupt's, + // MSP430Interrupt's, MipsInterrupt's and Z80Interrupt's spellings must match. let Spellings = [GCC<"interrupt">]; let Args = [EnumArgument<"Interrupt", "InterruptType", ["IRQ", "FIQ", "SWI", "ABORT", "UNDEF", ""], @@ -1451,8 +1452,8 @@ def MSABI : DeclOrTypeAttr { } def MSP430Interrupt : InheritableAttr, TargetSpecificAttr { - // NOTE: If you add any additional spellings, ARMInterrupt's, MipsInterrupt's - // and AnyX86Interrupt's spellings must match. + // NOTE: If you add any additional spellings, ARMInterrupt's, + // MSP430Interrupt's, MipsInterrupt's and Z80Interrupt's spellings must match. let Spellings = [GCC<"interrupt">]; let Args = [UnsignedArgument<"Number">]; let ParseKind = "Interrupt"; @@ -1468,7 +1469,7 @@ def Mips16 : InheritableAttr, TargetSpecificAttr { def MipsInterrupt : InheritableAttr, TargetSpecificAttr { // NOTE: If you add any additional spellings, ARMInterrupt's, - // MSP430Interrupt's and AnyX86Interrupt's spellings must match. + // MSP430Interrupt's, MipsInterrupt's and Z80Interrupt's spellings must match. let Spellings = [GCC<"interrupt">]; let Subjects = SubjectList<[Function]>; let Args = [EnumArgument<"Interrupt", "InterruptType", @@ -1500,6 +1501,26 @@ def MipsShortCall : InheritableAttr, TargetSpecificAttr { let Documentation = [MipsShortCallStyleDocs]; } +def AnyZ80Interrupt : InheritableAttr, TargetSpecificAttr { + // NOTE: If you add any additional spellings, MSP430Interrupt's, + // MipsInterrupt's and AnyX86Interrupt's spellings must match. + let Spellings = [GNU<"interrupt">]; + let Subjects = SubjectList<[Function]>; + let Args = [EnumArgument<"Interrupt", "InterruptType", + ["", "nested", "nmi"], + ["Generic", "Nested", "NMI"], + 1>]; + let ParseKind = "Interrupt"; + let HasCustomParsing = 1; + let Documentation = [Undocumented]; +} + +def AnyZ80TIFlags : DeclOrTypeAttr, TargetSpecificAttr { + let Spellings = [Clang<"tiflags">]; + let Subjects = SubjectList<[FunctionLike]>; + let Documentation = [AnyZ80TIFlagsDocs]; +} + def Mode : Attr { let Spellings = [GCC<"mode">]; let Subjects = SubjectList<[Var, Enum, TypedefName, Field], ErrorDiag>; @@ -2554,7 +2575,7 @@ def LTOVisibilityPublic : InheritableAttr { def AnyX86Interrupt : InheritableAttr, TargetSpecificAttr { // NOTE: If you add any additional spellings, ARMInterrupt's, - // MSP430Interrupt's and MipsInterrupt's spellings must match. + // MSP430Interrupt's, MipsInterrupt's and Z80Interrupt's spellings must match. let Spellings = [GCC<"interrupt">]; let Subjects = SubjectList<[HasFunctionProto]>; let ParseKind = "Interrupt"; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 3cba3a3d96f967..773cd53511e01e 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3703,6 +3703,15 @@ pointer (by adding nocf_check prefix to the indirect-call instruction). }]; } +def AnyZ80TIFlagsDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +TIOS, which runs on (e)Z80 processors, have a lot of system routines that expect +the IY register to contain a specific value. This attribute marks calls whose +calling convention should be changed appropriately. +}]; +} + def SwiftCallDocs : Documentation { let Category = DocCatVariable; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 70b43f5abc4b63..2293410c2fa2b9 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -289,6 +289,9 @@ def err_anyx86_interrupt_attribute : Error< "a pointer as the first parameter|a %2 type as the second parameter}1">; def err_anyx86_interrupt_called : Error< "interrupt service routine cannot be called directly">; +def err_anyz80_interrupt_attribute : Error< + "%select{z80|ez80}0 'interrupt' attribute only applies to functions that " + "have %select{a 'void' return type|no parameters}1">; def warn_arm_interrupt_calling_convention : Warning< "call to function without interrupt attribute could clobber interruptee's VFP registers">, InGroup; diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h index 2834dea20d0020..99ee221c13a0f2 100644 --- a/clang/include/clang/Basic/Specifiers.h +++ b/clang/include/clang/Basic/Specifiers.h @@ -66,6 +66,7 @@ namespace clang { TST_char16, // C++11 char16_t TST_char32, // C++11 char32_t TST_int, + TST_int48, TST_int128, TST_extint, // Extended Int types. TST_half, // OpenCL half, ARM NEON __fp16 diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index 140f55ff66b10a..2f82e6f0312851 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -59,6 +59,7 @@ namespace Builtin { struct Info; } struct TransferrableTargetInfo { unsigned char PointerWidth, PointerAlign; unsigned char BoolWidth, BoolAlign; + unsigned char ShortWidth, ShortAlign; unsigned char IntWidth, IntAlign; unsigned char HalfWidth, HalfAlign; unsigned char BFloat16Width, BFloat16Align; @@ -418,13 +419,10 @@ class TargetInfo : public virtual TransferrableTargetInfo, unsigned getCharWidth() const { return 8; } // FIXME unsigned getCharAlign() const { return 8; } // FIXME - /// Return the size of 'signed short' and 'unsigned short' for this - /// target, in bits. - unsigned getShortWidth() const { return 16; } // FIXME - - /// Return the alignment of 'signed short' and 'unsigned short' for - /// this target. - unsigned getShortAlign() const { return 16; } // FIXME + /// getShortWidth/Align - Return the size of 'signed short' and + /// 'unsigned short' for this target, in bits. + unsigned getShortWidth() const { return ShortWidth; } + unsigned getShortAlign() const { return ShortAlign; } /// getIntWidth/Align - Return the size of 'signed int' and 'unsigned int' for /// this target, in bits. @@ -554,6 +552,11 @@ class TargetInfo : public virtual TransferrableTargetInfo, : getLongFractScale() + 1; } + /// Determine whether the __int48 type is supported on this target. + virtual bool hasInt48Type() const { + return false; + } // FIXME + /// Determine whether the __int128 type is supported on this target. virtual bool hasInt128Type() const { return (getPointerWidth(0) >= 64) || getTargetOpts().ForceEnableInt128; diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 2b353269ed52d9..33ba8f4e173410 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -437,6 +437,7 @@ KEYWORD(__builtin_va_arg , KEYALL) KEYWORD(__extension__ , KEYALL) KEYWORD(__float128 , KEYALL) KEYWORD(__imag , KEYALL) +KEYWORD(__int48 , KEYALL) KEYWORD(__int128 , KEYALL) KEYWORD(__label__ , KEYALL) KEYWORD(__real , KEYALL) diff --git a/clang/include/clang/CodeGen/CGFunctionInfo.h b/clang/include/clang/CodeGen/CGFunctionInfo.h index eaf5a3d5aad710..faaf4a1e9248a4 100644 --- a/clang/include/clang/CodeGen/CGFunctionInfo.h +++ b/clang/include/clang/CodeGen/CGFunctionInfo.h @@ -527,6 +527,9 @@ class CGFunctionInfo final /// Whether this function has nocf_check attribute. unsigned NoCfCheck : 1; + /// Whether this function has tiflags attribute. + unsigned TIFlags : 1; + RequiredArgs Required; /// The struct representing all arguments passed in memory. Only used when @@ -615,6 +618,9 @@ class CGFunctionInfo final /// Whether this function has nocf_check attribute. bool isNoCfCheck() const { return NoCfCheck; } + /// Whether this function has tiflags attribute. + bool isTIFlags() const { return TIFlags; } + /// getASTCallingConvention() - Return the AST-specified calling /// convention. CallingConv getASTCallingConvention() const { @@ -641,7 +647,7 @@ class CGFunctionInfo final return FunctionType::ExtInfo(isNoReturn(), getHasRegParm(), getRegParm(), getASTCallingConvention(), isReturnsRetained(), isNoCallerSavedRegs(), isNoCfCheck(), - isCmseNSCall()); + isCmseNSCall(), isTIFlags()); } CanQualType getReturnType() const { return getArgsBuffer()[0].type; } @@ -683,6 +689,7 @@ class CGFunctionInfo final ID.AddInteger(RegParm); ID.AddBoolean(NoCfCheck); ID.AddBoolean(CmseNSCall); + ID.AddBoolean(TIFlags); ID.AddInteger(Required.getOpaqueData()); ID.AddBoolean(HasExtParameterInfos); if (HasExtParameterInfos) { @@ -711,6 +718,7 @@ class CGFunctionInfo final ID.AddInteger(info.getRegParm()); ID.AddBoolean(info.getNoCfCheck()); ID.AddBoolean(info.getCmseNSCall()); + ID.AddBoolean(info.getTIFlags()); ID.AddInteger(required.getOpaqueData()); ID.AddBoolean(!paramInfos.empty()); if (!paramInfos.empty()) { diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index eca822c6afa34f..30431c0a347798 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -159,6 +159,8 @@ def m_x86_Features_Group : OptionGroup<"">, Group, Flags<[CoreOption]>, DocName<"X86">; def m_riscv_Features_Group : OptionGroup<"">, Group, DocName<"RISCV">; +def m_z80_Features_Group : OptionGroup<"">, + Group, Flags<[CoreOption]>, DocName<"Z80">; def m_libc_Group : OptionGroup<"">, Group, Flags<[HelpHidden]>; diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 8db03babfb1e97..9ac8db7b746c5a 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -278,6 +278,7 @@ class DeclSpec { static const TST TST_char16 = clang::TST_char16; static const TST TST_char32 = clang::TST_char32; static const TST TST_int = clang::TST_int; + static const TST TST_int48 = clang::TST_int48; static const TST TST_int128 = clang::TST_int128; static const TST TST_extint = clang::TST_extint; static const TST TST_half = clang::TST_half; diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index c6f9f1d1a08f4b..e8a27f542fb71a 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1080,6 +1080,12 @@ class TypeIdx { // \brief SVE types with auto numeration #define SVE_TYPE(Name, Id, SingletonId) PREDEF_TYPE_##Id##_ID, #include "clang/Basic/AArch64SVEACLETypes.def" + + /// The '__uint48_t' type. + PREDEF_TYPE_UINT48_ID, + + /// The '__int48_t' type. + PREDEF_TYPE_INT48_ID, }; /// The number of predefined type IDs that are reserved for @@ -1198,6 +1204,13 @@ class TypeIdx { /// The internal '__type_pack_element' template. PREDEF_DECL_TYPE_PACK_ELEMENT_ID = 17, + + /// The signed 48-bit integer type. + PREDEF_DECL_INT_48_ID = 18, + + /// The unsigned 48-bit integer type. + PREDEF_DECL_UNSIGNED_INT_48_ID = 19, + }; /// The number of declaration IDs that are predefined. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 2ba643f12a82fa..60b3eea54b3f68 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1234,6 +1234,18 @@ TypedefDecl *ASTContext::buildImplicitTypedef(QualType T, return NewDecl; } +TypedefDecl *ASTContext::getInt48Decl() const { + if (!Int48Decl) + Int48Decl = buildImplicitTypedef(Int48Ty, "__int48_t"); + return Int48Decl; +} + +TypedefDecl *ASTContext::getUInt48Decl() const { + if (!UInt48Decl) + UInt48Decl = buildImplicitTypedef(UnsignedInt48Ty, "__uint48_t"); + return UInt48Decl; +} + TypedefDecl *ASTContext::getInt128Decl() const { if (!Int128Decl) Int128Decl = buildImplicitTypedef(Int128Ty, "__int128_t"); @@ -1326,6 +1338,10 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target, InitBuiltinType(SatUnsignedFractTy, BuiltinType::SatUFract); InitBuiltinType(SatUnsignedLongFractTy, BuiltinType::SatULongFract); + // eZ80 extension, 48-bit integers. + InitBuiltinType(Int48Ty, BuiltinType::Int48); + InitBuiltinType(UnsignedInt48Ty, BuiltinType::UInt48); + // GNU extension, 128-bit integers. InitBuiltinType(Int128Ty, BuiltinType::Int128); InitBuiltinType(UnsignedInt128Ty, BuiltinType::UInt128); @@ -1997,6 +2013,11 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { Width = Target->getLongWidth(); Align = Target->getLongAlign(); break; + case BuiltinType::UInt48: + case BuiltinType::Int48: + Width = 48; + Align = 8; // int48_t is 8-bit aligned on all (e)Z80 targets. + break; case BuiltinType::ULongLong: case BuiltinType::LongLong: Width = Target->getLongLongWidth(); @@ -6068,28 +6089,33 @@ unsigned ASTContext::getIntegerRank(const Type *T) const { switch (cast(T)->getKind()) { default: llvm_unreachable("getIntegerRank(): not a built-in integer"); + // Standard Integer Types case BuiltinType::Bool: - return 1 + (getIntWidth(BoolTy) << 3); + return 1 + (1 << 3) + (getIntWidth(BoolTy) << 4); case BuiltinType::Char_S: case BuiltinType::Char_U: case BuiltinType::SChar: case BuiltinType::UChar: - return 2 + (getIntWidth(CharTy) << 3); + return 2 + (1 << 3) + (getIntWidth(CharTy) << 4); case BuiltinType::Short: case BuiltinType::UShort: - return 3 + (getIntWidth(ShortTy) << 3); + return 3 + (1 << 3) + (getIntWidth(ShortTy) << 4); case BuiltinType::Int: case BuiltinType::UInt: - return 4 + (getIntWidth(IntTy) << 3); + return 4 + (1 << 3) + (getIntWidth(IntTy) << 4); case BuiltinType::Long: case BuiltinType::ULong: - return 5 + (getIntWidth(LongTy) << 3); + return 5 + (1 << 3) + (getIntWidth(LongTy) << 4); case BuiltinType::LongLong: case BuiltinType::ULongLong: - return 6 + (getIntWidth(LongLongTy) << 3); + return 6 + (1 << 3) + (getIntWidth(LongLongTy) << 4); + // Extended Integer Types + case BuiltinType::Int48: + case BuiltinType::UInt48: + return 1 + (getIntWidth(Int48Ty) << 4); case BuiltinType::Int128: case BuiltinType::UInt128: - return 7 + (getIntWidth(Int128Ty) << 3); + return 2 + (getIntWidth(Int128Ty) << 4); } } @@ -7030,6 +7056,8 @@ static char getObjCEncodingForPrimitiveType(const ASTContext *C, case BuiltinType::SatUShortFract: case BuiltinType::SatUFract: case BuiltinType::SatULongFract: + case BuiltinType::UInt48: + case BuiltinType::Int48: // FIXME: potentially need @encodes for these! return ' '; @@ -9111,6 +9139,8 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, return {}; if (lbaseInfo.getNoCfCheck() != rbaseInfo.getNoCfCheck()) return {}; + if (lbaseInfo.getTIFlags() != rbaseInfo.getTIFlags()) + return {}; // FIXME: some uses, e.g. conditional exprs, really want this to be 'both'. bool NoReturn = lbaseInfo.getNoReturn() || rbaseInfo.getNoReturn(); @@ -9756,6 +9786,8 @@ QualType ASTContext::getCorrespondingUnsignedType(QualType T) const { return UnsignedIntTy; case BuiltinType::Long: return UnsignedLongTy; + case BuiltinType::Int48: + return UnsignedInt48Ty; case BuiltinType::LongLong: return UnsignedLongLongTy; case BuiltinType::Int128: @@ -10666,8 +10698,12 @@ QualType ASTContext::getIntTypeForBitwidth(unsigned DestWidth, unsigned Signed) const { TargetInfo::IntType Ty = getTargetInfo().getIntTypeByWidth(DestWidth, Signed); CanQualType QualTy = getFromTargetType(Ty); - if (!QualTy && DestWidth == 128) - return Signed ? Int128Ty : UnsignedInt128Ty; + if (!QualTy) { + if (DestWidth == 128) + return Signed ? Int128Ty : UnsignedInt128Ty; + if (DestWidth == 48) + return Signed ? Int48Ty : UnsignedInt48Ty; + } return QualTy; } diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index 8b5b2444f1e25b..3a5504b111c1be 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -391,6 +391,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return false; if (EI1.getNoCfCheck() != EI2.getNoCfCheck()) return false; + if (EI1.getTIFlags() != EI2.getTIFlags()) + return false; return true; } diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index afae020f3dd272..6a47cd2cc94426 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -10481,6 +10481,7 @@ EvaluateBuiltinClassifyType(QualType T, const LangOptions &LangOpts) { case BuiltinType::UShort: case BuiltinType::UInt: case BuiltinType::ULong: + case BuiltinType::UInt48: case BuiltinType::ULongLong: case BuiltinType::UInt128: return GCCTypeClass::Integer; diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index ddfbe9f864991f..ccf96324f1f8a7 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2835,6 +2835,12 @@ void CXXNameMangler::mangleType(const BuiltinType *T) { << type_name; \ break; #include "clang/Basic/AArch64SVEACLETypes.def" + case BuiltinType::Int48: + Out << "9z80_int48"; + break; + case BuiltinType::UInt48: + Out << "10z80_uint48"; + break; } } diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index 529f301e469644..ce6f5689826e8b 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -2115,7 +2115,9 @@ void MicrosoftCXXNameMangler::mangleType(const BuiltinType *T, Qualifiers, case BuiltinType::SatUFract: case BuiltinType::SatULongFract: case BuiltinType::BFloat16: - case BuiltinType::Float128: { + case BuiltinType::Float128: + case BuiltinType::Int48: + case BuiltinType::UInt48: { DiagnosticsEngine &Diags = Context.getDiags(); unsigned DiagID = Diags.getCustomDiagID( DiagnosticsEngine::Error, "cannot mangle this built-in %0 type yet"); diff --git a/clang/lib/AST/NSAPI.cpp b/clang/lib/AST/NSAPI.cpp index ace7f1ceebe74e..58d9e6e80cd555 100644 --- a/clang/lib/AST/NSAPI.cpp +++ b/clang/lib/AST/NSAPI.cpp @@ -487,6 +487,8 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) const { case BuiltinType::OMPArrayShaping: case BuiltinType::OMPIterator: case BuiltinType::BFloat16: + case BuiltinType::Int48: + case BuiltinType::UInt48: break; } diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp index f3ac181214ac62..89928d02994039 100644 --- a/clang/lib/AST/PrintfFormatString.cpp +++ b/clang/lib/AST/PrintfFormatString.cpp @@ -749,6 +749,8 @@ bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, case BuiltinType::Char8: // FIXME: Treat like 'char'? case BuiltinType::Char16: case BuiltinType::Char32: + case BuiltinType::UInt48: + case BuiltinType::Int48: case BuiltinType::UInt128: case BuiltinType::Int128: case BuiltinType::Half: diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 05962f34bbf1c3..01aa70ec66677a 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2905,6 +2905,8 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const { return "int"; case Long: return "long"; + case Int48: + return "__int48"; case LongLong: return "long long"; case Int128: @@ -2917,6 +2919,8 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const { return "unsigned int"; case ULong: return "unsigned long"; + case UInt48: + return "unsigned __int48"; case ULongLong: return "unsigned long long"; case UInt128: diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp index 57c11ca5571db9..6e498b798a4aba 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -376,6 +376,8 @@ TypeSpecifierType BuiltinTypeLoc::getWrittenTypeSpec() const { case BuiltinType::SatUFract: case BuiltinType::SatULongFract: case BuiltinType::BFloat16: + case BuiltinType::UInt48: + case BuiltinType::Int48: llvm_unreachable("Builtin type needs extra local data!"); // Fall through, if the impossible happens. diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 6f6932e652146e..46af1c49cb5f62 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -956,6 +956,8 @@ void TypePrinter::printFunctionAfter(const FunctionType::ExtInfo &Info, OS << " __attribute__((no_caller_saved_registers))"; if (Info.getNoCfCheck()) OS << " __attribute__((nocf_check))"; + if (Info.getTIFlags()) + OS << " __attribute__((tiflags))"; } void TypePrinter::printFunctionNoProtoBefore(const FunctionNoProtoType *T, @@ -1594,6 +1596,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, // FIXME: When Sema learns to form this AttributedType, avoid printing the // attribute again in printFunctionProtoAfter. case attr::AnyX86NoCfCheck: OS << "nocf_check"; break; + case attr::AnyZ80TIFlags: OS << "tiflags"; break; case attr::CDecl: OS << "cdecl"; break; case attr::FastCall: OS << "fastcall"; break; case attr::StdCall: OS << "stdcall"; break; diff --git a/clang/lib/Basic/CMakeLists.txt b/clang/lib/Basic/CMakeLists.txt index 1b55d841737714..8cda98c5a1c93e 100644 --- a/clang/lib/Basic/CMakeLists.txt +++ b/clang/lib/Basic/CMakeLists.txt @@ -88,6 +88,7 @@ add_clang_library(clangBasic Targets/WebAssembly.cpp Targets/X86.cpp Targets/XCore.cpp + Targets/Z80.cpp TokenKinds.cpp TypeTraits.cpp Version.cpp diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 7f360b715da905..c907e8f3019426 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -39,6 +39,7 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : TargetOpts(), Triple(T) { HasBFloat16 = false; PointerWidth = PointerAlign = 32; BoolWidth = BoolAlign = 8; + ShortWidth = ShortAlign = 16; IntWidth = IntAlign = 32; LongWidth = LongAlign = 32; LongLongWidth = LongLongAlign = 64; diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp index 6bbcafa27dfe64..1d8cd5e5d972c7 100644 --- a/clang/lib/Basic/Targets.cpp +++ b/clang/lib/Basic/Targets.cpp @@ -37,6 +37,7 @@ #include "Targets/WebAssembly.h" #include "Targets/X86.h" #include "Targets/XCore.h" +#include "Targets/Z80.h" #include "clang/Basic/Diagnostic.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" @@ -617,6 +618,11 @@ TargetInfo *AllocateTarget(const llvm::Triple &Triple, case llvm::Triple::ve: return new LinuxTargetInfo(Triple, Opts); + + case llvm::Triple::z80: + return new Z80TargetInfo(Triple, Opts); + case llvm::Triple::ez80: + return new EZ80TargetInfo(Triple, Opts); } } } // namespace targets diff --git a/clang/lib/Basic/Targets/Z80.cpp b/clang/lib/Basic/Targets/Z80.cpp new file mode 100644 index 00000000000000..f33aa75fbc5a3a --- /dev/null +++ b/clang/lib/Basic/Targets/Z80.cpp @@ -0,0 +1,60 @@ +//===--- Z80.cpp - Implement Z80 target feature support -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements Z80 TargetInfo objects. +// +//===----------------------------------------------------------------------===// + +#include "Z80.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang; +using namespace clang::targets; + +bool Z80TargetInfo::setCPU(const std::string &Name) { + return llvm::StringSwitch(Name) + .Case("generic", true) + .Case("z80", true) + .Case("z180", true) + .Default(false); +} + +bool Z80TargetInfo:: +initFeatureMap(llvm::StringMap &Features, + DiagnosticsEngine &Diags, StringRef CPU, + const std::vector &FeaturesVec) const { + if (CPU == "z80") + Features["undoc"] = true; + if (CPU == "z180") + Features["z180"] = true; + return TargetInfo::initFeatureMap(Features, Diags, CPU, FeaturesVec); +} + +void Z80TargetInfo::getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const { + Z80TargetInfoBase::getTargetDefines(Opts, Builder); + defineCPUMacros(Builder, "Z80", /*Tuning=*/false); + if (getTargetOpts().CPU == "undoc") + defineCPUMacros(Builder, "Z80_UNDOC", /*Tuning=*/false); + else if (getTargetOpts().CPU == "z180") + defineCPUMacros(Builder, "Z180", /*Tuning=*/false); +} + +bool EZ80TargetInfo::setCPU(const std::string &Name) { + return llvm::StringSwitch(Name) + .Case("generic", true) + .Case("ez80", true) + .Default(false); +} + +void EZ80TargetInfo::getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const { + Z80TargetInfoBase::getTargetDefines(Opts, Builder); + defineCPUMacros(Builder, "EZ80", /*Tuning=*/false); +} diff --git a/clang/lib/Basic/Targets/Z80.h b/clang/lib/Basic/Targets/Z80.h new file mode 100644 index 00000000000000..0fff1261334810 --- /dev/null +++ b/clang/lib/Basic/Targets/Z80.h @@ -0,0 +1,102 @@ +//===--- Z80.h - Declare Z80 target feature support -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares Z80 TargetInfo objects. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_BASIC_TARGETS_Z80_H +#define LLVM_CLANG_LIB_BASIC_TARGETS_Z80_H + +#include "Targets.h" +#include "clang/Basic/TargetInfo.h" +#include "llvm/Support/Compiler.h" + +namespace clang { +namespace targets { + +class LLVM_LIBRARY_VISIBILITY Z80TargetInfoBase : public TargetInfo { +public: + Z80TargetInfoBase(const llvm::Triple &Triple, const TargetOptions &) + : TargetInfo(Triple) { + TLSSupported = false; + PointerAlign = BoolAlign = ShortAlign = IntAlign = HalfAlign = FloatAlign = + DoubleAlign = LongDoubleAlign = LongAlign = LongLongAlign = + SuitableAlign = MinGlobalAlign = 8; + DoubleWidth = 32; + DoubleFormat = &llvm::APFloat::IEEEsingle(); + DefaultAlignForAttributeAligned = 32; + SizeType = UnsignedInt; + WCharType = SignedShort; + PtrDiffType = IntPtrType = WIntType = SignedInt; + Char32Type = UnsignedLong; + } + bool hasInt48Type() const override { return true; } + ArrayRef getTargetBuiltins() const final { return None; } + BuiltinVaListKind getBuiltinVaListKind() const override { + return TargetInfo::CharPtrBuiltinVaList; + } + bool validateAsmConstraint(const char *&Name, + TargetInfo::ConstraintInfo &Info) const override { + return false; + } + const char *getClobbers() const override { return ""; } + ArrayRef getGCCRegNames() const override { return None; } + ArrayRef getGCCRegAliases() const override { + return None; + } + + void getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const override { + } + + bool allowsLargerPreferedTypeAlignment() const override { return false; } +}; + +class LLVM_LIBRARY_VISIBILITY Z80TargetInfo : public Z80TargetInfoBase { +public: + explicit Z80TargetInfo(const llvm::Triple &T, const TargetOptions &Opts) + : Z80TargetInfoBase(T, Opts) { + PointerWidth = IntWidth = 16; + resetDataLayout("e-m:o-p:16:8-p1:8:8-i16:8-i24:8-i32:8-i48:8-i64:8-i96:8-f32:8-f64:8-a:8-n8:16-S8"); + } + +private: + bool setCPU(const std::string &Name) override; + bool + initFeatureMap(llvm::StringMap &Features, DiagnosticsEngine &Diags, + StringRef CPU, + const std::vector &FeaturesVec) const override; + void getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const override; +}; + +class LLVM_LIBRARY_VISIBILITY EZ80TargetInfo : public Z80TargetInfoBase { +public: + explicit EZ80TargetInfo(const llvm::Triple &T, const TargetOptions &Opts) + : Z80TargetInfoBase(T, Opts) { + if (T.getEnvironment() == llvm::Triple::CODE16) { + PointerWidth = IntWidth = 16; + resetDataLayout("e-m:o-p:16:8-p1:16:8-p2:24:8-i16:8-i24:8-i32:8-i48:8-i64:8-i96:8-f32:8-f64:8-a:8-n8:16-S8"); + } else { + PointerWidth = IntWidth = 24; + resetDataLayout("e-m:o-p:24:8-p1:16:8-p2:16:8-i16:8-i24:8-i32:8-i48:8-i64:8-i96:8-f32:8-f64:8-a:8-n8:16:24-S8"); + } + } + +private: + bool setCPU(const std::string &Name) override; + void getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const override; +}; + +} // namespace targets +} // namespace clang + +#endif // LLVM_CLANG_LIB_BASIC_TARGETS_Z80_H diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 87242442a57f8a..3e715ff0f48fa7 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -821,6 +821,7 @@ CGFunctionInfo *CGFunctionInfo::create(unsigned llvmCC, FI->ReturnsRetained = info.getProducesResult(); FI->NoCallerSavedRegs = info.getNoCallerSavedRegs(); FI->NoCfCheck = info.getNoCfCheck(); + FI->TIFlags = info.getTIFlags(); FI->Required = required; FI->HasRegParm = info.getHasRegParm(); FI->RegParm = info.getRegParm(); @@ -1975,6 +1976,8 @@ void CodeGenModule::ConstructAttributeList( FuncAttrs.addAttribute("no_caller_saved_registers"); if (TargetDecl->hasAttr()) FuncAttrs.addAttribute(llvm::Attribute::NoCfCheck); + if (TargetDecl->hasAttr()) + CallingConv = llvm::CallingConv::Z80_TIFlags; HasOptnone = TargetDecl->hasAttr(); if (auto *AllocSize = TargetDecl->getAttr()) { diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 6965c4a1209c22..c907b4d1296fa7 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -751,6 +751,7 @@ llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) { break; case BuiltinType::UShort: case BuiltinType::UInt: + case BuiltinType::UInt48: case BuiltinType::UInt128: case BuiltinType::ULong: case BuiltinType::WChar_U: @@ -759,6 +760,7 @@ llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) { break; case BuiltinType::Short: case BuiltinType::Int: + case BuiltinType::Int48: case BuiltinType::Int128: case BuiltinType::Long: case BuiltinType::WChar_S: diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp index d431c0263666ea..a85461b8849290 100644 --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/clang/lib/CodeGen/CodeGenTypes.cpp @@ -515,6 +515,11 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) { ResultType = llvm::Type::getInt8PtrTy(getLLVMContext()); break; + case BuiltinType::UInt48: + case BuiltinType::Int48: + ResultType = llvm::IntegerType::get(getLLVMContext(), 48); + break; + case BuiltinType::UInt128: case BuiltinType::Int128: ResultType = llvm::IntegerType::get(getLLVMContext(), 128); diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 2829877cfe5d91..16637225c5007e 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -3103,6 +3103,8 @@ static bool TypeInfoIsInStandardLibrary(const BuiltinType *Ty) { case BuiltinType::SatUFract: case BuiltinType::SatULongFract: case BuiltinType::BFloat16: + case BuiltinType::Int48: + case BuiltinType::UInt48: return false; case BuiltinType::Dependent: diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index 24237c4606877e..595d69a147cd42 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -10620,6 +10620,85 @@ class VETargetCodeGenInfo : public TargetCodeGenInfo { }; } // end anonymous namespace +//===----------------------------------------------------------------------===// +// Z80 ABI Implementation +//===----------------------------------------------------------------------===// + +namespace { +class Z80ABIInfo : public DefaultABIInfo { +public: + Z80ABIInfo(CodeGenTypes &CGT) : DefaultABIInfo(CGT) {} + +private: + void removeExtend(ABIArgInfo &AI) const { + if (AI.isExtend()) { + bool InReg = AI.getInReg(); + AI = ABIArgInfo::getDirect(AI.getCoerceToType()); + AI.setInReg(InReg); + } + } + // DefaultABIInfo's classifyReturnType and classifyArgumentType are + // non-virtual, but computeInfo and EmitVAArg are virtual, so we + // overload them. + void computeInfo(CGFunctionInfo &FI) const override { + if (!getCXXABI().classifyReturnType(FI)) + FI.getReturnInfo() = classifyReturnType(FI.getReturnType()); + removeExtend(FI.getReturnInfo()); + for (auto &Arg : FI.arguments()) + removeExtend(Arg.info = classifyArgumentType(Arg.type)); + } + + Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, + QualType Ty) const override; +}; + +class Z80TargetCodeGenInfo : public TargetCodeGenInfo { +public: + Z80TargetCodeGenInfo(CodeGen::CodeGenTypes &CGT) + : TargetCodeGenInfo(std::make_unique(CGT)) {} + void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV, + CodeGen::CodeGenModule &CGM) const override; +}; +} // namespace + +Address Z80ABIInfo::EmitVAArg(CodeGenFunction &CGF, + Address VAListAddr, QualType Ty) const { + Address Addr = emitVoidPtrVAArg( + CGF, VAListAddr, Ty, /*Indirect*/ false, + getContext().getTypeInfoInChars(Ty), + CharUnits::fromQuantity(getDataLayout().getPointerSize()), + /*AllowHigherAlign*/ false); + // Remove SlotSize over-alignment, since stack is never aligned. + return Address(Addr.getPointer(), CharUnits::fromQuantity(1)); +} + +void Z80TargetCodeGenInfo::setTargetAttributes( + const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &CGM) const { + const FunctionDecl *FD = dyn_cast_or_null(D); + if (!FD) return; + llvm::Function *Fn = cast(GV); + + if (Fn->isDeclaration()) { + if (FD->getAttr()) + Fn->setCallingConv(llvm::CallingConv::Z80_TIFlags); + return; + } + + const AnyZ80InterruptAttr *Attr = FD->getAttr(); + if (!Attr) + return; + + const char *Kind; + switch (Attr->getInterrupt()) { + case AnyZ80InterruptAttr::Generic: Kind = "Generic"; break; + case AnyZ80InterruptAttr::Nested: Kind = "Nested"; break; + case AnyZ80InterruptAttr::NMI: Kind = "NMI"; break; + } + + Fn->setCallingConv(llvm::CallingConv::PreserveAll); + Fn->addFnAttr("interrupt", Kind); +} + //===----------------------------------------------------------------------===// // Driver code //===----------------------------------------------------------------------===// @@ -10824,6 +10903,9 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { return SetCGInfo(new SPIRTargetCodeGenInfo(Types)); case llvm::Triple::ve: return SetCGInfo(new VETargetCodeGenInfo(Types)); + case llvm::Triple::z80: + case llvm::Triple::ez80: + return SetCGInfo(new Z80TargetCodeGenInfo(Types)); } } diff --git a/clang/lib/Driver/CMakeLists.txt b/clang/lib/Driver/CMakeLists.txt index b13789f8b2a8b6..96d8ac4272b688 100644 --- a/clang/lib/Driver/CMakeLists.txt +++ b/clang/lib/Driver/CMakeLists.txt @@ -33,6 +33,7 @@ add_clang_library(clangDriver ToolChains/Arch/SystemZ.cpp ToolChains/Arch/VE.cpp ToolChains/Arch/X86.cpp + ToolChains/Arch/Z80.cpp ToolChains/AIX.cpp ToolChains/Ananas.cpp ToolChains/AMDGPU.cpp diff --git a/clang/lib/Driver/ToolChains/Arch/Z80.cpp b/clang/lib/Driver/ToolChains/Arch/Z80.cpp new file mode 100644 index 00000000000000..b2e82fd5ffe14f --- /dev/null +++ b/clang/lib/Driver/ToolChains/Arch/Z80.cpp @@ -0,0 +1,49 @@ +//===--- Z80.cpp - Z80 Helpers for Tools ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Z80.h" +#include "ToolChains/CommonArgs.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/Host.h" + +using namespace clang::driver; +using namespace clang::driver::tools; +using namespace clang; +using namespace llvm::opt; + +const char *z80::getZ80TargetCPU(const ArgList &Args, + const llvm::Triple &Triple) { + if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) + return A->getValue(); + + // Select the default CPU if none was given (or detection failed). + + if (!Triple.isZ80()) + return nullptr; // This routine is only handling z80 targets. + + bool Is24Bit = Triple.getArch() == llvm::Triple::ez80; + + // Everything else goes to ez80 in 24-bit mode. + if (Is24Bit) + return "ez80"; + + // Fallback to generic + return "z80"; +} + +void z80::getZ80TargetFeatures(const Driver &D, const llvm::Triple &Triple, + const ArgList &Args, + std::vector &Features) { + // Now add any that the user explicitly requested on the command line, + // which may override the defaults. + handleTargetFeaturesGroup(Args, Features, options::OPT_m_z80_Features_Group); +} diff --git a/clang/lib/Driver/ToolChains/Arch/Z80.h b/clang/lib/Driver/ToolChains/Arch/Z80.h new file mode 100644 index 00000000000000..498cbfb829ba80 --- /dev/null +++ b/clang/lib/Driver/ToolChains/Arch/Z80.h @@ -0,0 +1,36 @@ +//===--- Z80.h - Z80-specific Tool Helpers ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_ARCH_Z80_H +#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_ARCH_Z80_H + +#include "clang/Driver/Driver.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Option/Option.h" +#include +#include + +namespace clang { +namespace driver { +namespace tools { +namespace z80 { + +const char *getZ80TargetCPU(const llvm::opt::ArgList &Args, + const llvm::Triple &Triple); + +void getZ80TargetFeatures(const Driver &D, const llvm::Triple &Triple, + const llvm::opt::ArgList &Args, + std::vector &Features); + +} // end namespace z80 +} // end namespace target +} // end namespace driver +} // end namespace clang + +#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_ARCH_Z80_H diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 8903641a26c6f9..925b9be282eb0d 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -533,6 +533,8 @@ static bool useFramePointerForTargetByDefault(const ArgList &Args, case llvm::Triple::riscv64: case llvm::Triple::amdgcn: case llvm::Triple::r600: + case llvm::Triple::z80: + case llvm::Triple::ez80: return !areOptimizationsEnabled(Args); default: break; diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index 976db3feb9fcd7..b516bead3b2311 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -14,6 +14,7 @@ #include "Arch/SystemZ.h" #include "Arch/VE.h" #include "Arch/X86.h" +#include "Arch/Z80.h" #include "HIP.h" #include "Hexagon.h" #include "InputInfo.h" @@ -364,6 +365,10 @@ std::string tools::getCPUName(const ArgList &Args, const llvm::Triple &T, case llvm::Triple::wasm32: case llvm::Triple::wasm64: return std::string(getWebAssemblyTargetCPU(Args)); + + case llvm::Triple::z80: + case llvm::Triple::ez80: + return std::string(z80::getZ80TargetCPU(Args, T)); } } diff --git a/clang/lib/Format/FormatToken.cpp b/clang/lib/Format/FormatToken.cpp index 7d792974cd577a..457e24de01efc8 100644 --- a/clang/lib/Format/FormatToken.cpp +++ b/clang/lib/Format/FormatToken.cpp @@ -41,6 +41,7 @@ bool FormatToken::isSimpleTypeSpecifier() const { case tok::kw_short: case tok::kw_long: case tok::kw___int64: + case tok::kw___int48: case tok::kw___int128: case tok::kw_signed: case tok::kw_unsigned: diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 6eef1e2376f6da..fc01868392c3f9 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -770,6 +770,9 @@ static void InitializePredefinedMacros(const TargetInfo &TI, if (LangOpts.FastMath) Builder.defineMacro("__FAST_MATH__"); + if (!LangOpts.CharIsSigned) + Builder.defineMacro("__CHAR_UNSIGNED__"); + // Initialize target-specific preprocessor defines. // __BYTE_ORDER__ was added in GCC 4.6. It's analogous @@ -803,12 +806,22 @@ static void InitializePredefinedMacros(const TargetInfo &TI, // Define type sizing macros based on the target properties. assert(TI.getCharWidth() == 8 && "Only support 8-bit char so far"); Builder.defineMacro("__CHAR_BIT__", Twine(TI.getCharWidth())); + DefineTypeSize("__CHAR_MAX__", + LangOpts.CharIsSigned ? TargetInfo::SignedChar + : TargetInfo::UnsignedChar, TI, Builder); DefineTypeSize("__SCHAR_MAX__", TargetInfo::SignedChar, TI, Builder); DefineTypeSize("__SHRT_MAX__", TargetInfo::SignedShort, TI, Builder); DefineTypeSize("__INT_MAX__", TargetInfo::SignedInt, TI, Builder); DefineTypeSize("__LONG_MAX__", TargetInfo::SignedLong, TI, Builder); DefineTypeSize("__LONG_LONG_MAX__", TargetInfo::SignedLongLong, TI, Builder); + + DefineTypeSize("__UCHAR_MAX__", TargetInfo::UnsignedChar, TI, Builder); + DefineTypeSize("__USHRT_MAX__", TargetInfo::UnsignedShort, TI, Builder); + DefineTypeSize("__UINT_MAX__", TargetInfo::UnsignedInt, TI, Builder); + DefineTypeSize("__ULONG_MAX__", TargetInfo::UnsignedLong, TI, Builder); + DefineTypeSize("__ULONG_LONG_MAX__", TargetInfo::UnsignedLongLong, TI, Builder); + DefineTypeSize("__WCHAR_MAX__", TI.getWCharType(), TI, Builder); DefineTypeSize("__WINT_MAX__", TI.getWIntType(), TI, Builder); DefineTypeSize("__INTMAX_MAX__", TI.getIntMaxType(), TI, Builder); @@ -835,6 +848,8 @@ static void InitializePredefinedMacros(const TargetInfo &TI, TI.getTypeWidth(TI.getWCharType()), TI, Builder); DefineTypeSizeof("__SIZEOF_WINT_T__", TI.getTypeWidth(TI.getWIntType()), TI, Builder); + if (TI.hasInt48Type()) + DefineTypeSizeof("__SIZEOF_INT48__", 48, TI, Builder); if (TI.hasInt128Type()) DefineTypeSizeof("__SIZEOF_INT128__", 128, TI, Builder); @@ -1116,6 +1131,14 @@ static void InitializePredefinedMacros(const TargetInfo &TI, Builder.defineMacro("__IMAGE_SUPPORT__"); } + if (TI.hasInt48Type() && LangOpts.CPlusPlus && LangOpts.GNUMode) { + // For each extended integer type, g++ defines a macro mapping the + // index of the type (0 in this case) in some list of extended types + // to the type. + Builder.defineMacro("__GLIBCXX_TYPE_INT_N_0", "__int48"); + Builder.defineMacro("__GLIBCXX_BITSIZE_INT_N_0", "48"); + } + if (TI.hasInt128Type() && LangOpts.CPlusPlus && LangOpts.GNUMode) { // For each extended integer type, g++ defines a macro mapping the // index of the type (0 in this case) in some list of extended types diff --git a/clang/lib/Index/USRGeneration.cpp b/clang/lib/Index/USRGeneration.cpp index 0d1e8121982346..10fbc4f9793583 100644 --- a/clang/lib/Index/USRGeneration.cpp +++ b/clang/lib/Index/USRGeneration.cpp @@ -754,6 +754,8 @@ void USRGenerator::VisitType(QualType T) { case BuiltinType::SatUFract: case BuiltinType::SatULongFract: case BuiltinType::BFloat16: + case BuiltinType::UInt48: + case BuiltinType::Int48: IgnoreResults = true; return; case BuiltinType::ObjCId: diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index f336af6f1abcd8..a33e1bd8fe075a 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -3686,6 +3686,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, ConsumedEnd = PrevTokLocation; break; } + case tok::kw___int48: + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_int48, Loc, PrevSpec, + DiagID, Policy); + break; case tok::kw___int128: isInvalid = DS.SetTypeSpecType(DeclSpec::TST_int128, Loc, PrevSpec, DiagID, Policy); @@ -4783,6 +4787,7 @@ bool Parser::isKnownToBeTypeSpecifier(const Token &Tok) const { // type-specifiers case tok::kw_short: case tok::kw_long: + case tok::kw___int48: case tok::kw___int64: case tok::kw___int128: case tok::kw_signed: @@ -4864,6 +4869,7 @@ bool Parser::isTypeSpecifierQualifier() { // type-specifiers case tok::kw_short: case tok::kw_long: + case tok::kw___int48: case tok::kw___int64: case tok::kw___int128: case tok::kw_signed: @@ -5031,6 +5037,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { // type-specifiers case tok::kw_short: case tok::kw_long: + case tok::kw___int48: case tok::kw___int64: case tok::kw___int128: case tok::kw_signed: diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index f4c30c90ad271c..08079829acde34 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -359,6 +359,7 @@ bool Declarator::isDeclarationOfFunction() const { case TST_float: case TST_half: case TST_int: + case TST_int48: case TST_int128: case TST_extint: case TST_struct: @@ -539,6 +540,7 @@ const char *DeclSpec::getSpecifierName(DeclSpec::TST T, case DeclSpec::TST_char16: return "char16_t"; case DeclSpec::TST_char32: return "char32_t"; case DeclSpec::TST_int: return "int"; + case DeclSpec::TST_int48: return "__int48"; case DeclSpec::TST_int128: return "__int128"; case DeclSpec::TST_extint: return "_ExtInt"; case DeclSpec::TST_half: return "half"; @@ -1223,9 +1225,10 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) { if (TypeSpecSign != TSS_unspecified) { if (TypeSpecType == TST_unspecified) TypeSpecType = TST_int; // unsigned -> unsigned int, signed -> signed int. - else if (TypeSpecType != TST_int && TypeSpecType != TST_int128 && - TypeSpecType != TST_char && TypeSpecType != TST_wchar && - !IsFixedPointType && TypeSpecType != TST_extint) { + else if (TypeSpecType != TST_int && TypeSpecType != TST_int48 && + TypeSpecType != TST_int128 && TypeSpecType != TST_char && + TypeSpecType != TST_wchar && !IsFixedPointType && + TypeSpecType != TST_extint) { S.Diag(TSSLoc, diag::err_invalid_sign_spec) << getSpecifierName((TST)TypeSpecType, Policy); // signed double -> double. diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index fc3fb524c8f42a..05a294201666a4 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -235,6 +235,19 @@ void Sema::Initialize() { if (!TUScope) return; + // Initialize predefined 48-bit integer types, if needed. + if (Context.getTargetInfo().hasInt48Type()) { + // If either of the 48-bit integer types are unavailable to name lookup, + // define them now. + DeclarationName Int48 = &Context.Idents.get("__int48_t"); + if (IdResolver.begin(Int48) == IdResolver.end()) + PushOnScopeChains(Context.getInt48Decl(), TUScope); + + DeclarationName UInt48 = &Context.Idents.get("__uint48_t"); + if (IdResolver.begin(UInt48) == IdResolver.end()) + PushOnScopeChains(Context.getUInt48Decl(), TUScope); + } + // Initialize predefined 128-bit integer types, if needed. if (Context.getTargetInfo().hasInt128Type()) { // If either of the 128-bit integer types are unavailable to name lookup, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 852f204ea7b150..faf4e3c0a5b3bb 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -128,6 +128,7 @@ bool Sema::isSimpleTypeSpecifier(tok::TokenKind Kind) const { // token kind is a valid type specifier case tok::kw_short: case tok::kw_long: + case tok::kw___int48: case tok::kw___int64: case tok::kw___int128: case tok::kw_signed: diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 1a0594512a606f..92e25b8efbf160 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6023,6 +6023,19 @@ static void handleRISCVInterruptAttr(Sema &S, Decl *D, D->addAttr(::new (S.Context) RISCVInterruptAttr(S.Context, AL, Kind)); } +static void handleAnyZ80InterruptAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + if (!isFunctionOrMethod(D)) { + S.Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type) + << "'interrupt'" << ExpectedFunction; + return; + } + + if (!checkAttributeNumArgs(S, AL, 0)) + return; + + handleSimpleAttribute(S, D, AL); +} + static void handleInterruptAttr(Sema &S, Decl *D, const ParsedAttr &AL) { // Dispatch the interrupt attribute based on the current target. switch (S.Context.getTargetInfo().getTriple().getArch()) { @@ -6044,6 +6057,10 @@ static void handleInterruptAttr(Sema &S, Decl *D, const ParsedAttr &AL) { case llvm::Triple::riscv64: handleRISCVInterruptAttr(S, D, AL); break; + case llvm::Triple::z80: + case llvm::Triple::ez80: + handleAnyZ80InterruptAttr(S, D, AL); + break; default: handleARMInterruptAttr(S, D, AL); break; @@ -7058,6 +7075,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_AnyX86NoCfCheck: handleNoCfCheckAttr(S, D, AL); break; + case ParsedAttr::AT_AnyZ80TIFlags: + handleSimpleAttribute(S, D, AL); + break; case ParsedAttr::AT_NoThrow: if (!AL.isUsedAsTypeAttr()) handleSimpleAttribute(S, D, AL); diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp index 7b77d1cb482aee..c123165eca9497 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -861,6 +861,7 @@ bool Sema::containsUnexpandedParameterPacks(Declarator &D) { case TST_char16: case TST_char32: case TST_int: + case TST_int48: case TST_int128: case TST_half: case TST_float: diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 08d12fc25bf7aa..9e38d184c2a708 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -133,6 +133,7 @@ static void diagnoseBadTypeAttribute(Sema &S, const ParsedAttr &attr, case ParsedAttr::AT_CmseNSCall: \ case ParsedAttr::AT_AnyX86NoCallerSavedRegisters: \ case ParsedAttr::AT_AnyX86NoCfCheck: \ + case ParsedAttr::AT_AnyZ80TIFlags: \ CALLING_CONV_ATTRS_CASELIST // Microsoft-specific type qualifiers. @@ -1500,6 +1501,15 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { break; } + case DeclSpec::TST_int48: + if (!S.Context.getTargetInfo().hasInt48Type()) + S.Diag(DS.getTypeSpecTypeLoc(), diag::err_type_unsupported) + << "__int48"; + if (DS.getTypeSpecSign() == DeclSpec::TSS_unsigned) + Result = Context.UnsignedInt48Ty; + else + Result = Context.Int48Ty; + break; case DeclSpec::TST_int128: if (!S.Context.getTargetInfo().hasInt128Type() && !(S.getLangOpts().OpenMP && S.getLangOpts().OpenMPIsDevice)) @@ -7357,6 +7367,20 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr, return true; } + if (attr.getKind() == ParsedAttr::AT_AnyZ80TIFlags) { + if (S.CheckAttrTarget(attr) || S.CheckAttrNoArgs(attr)) + return true; + + // If this is not a function type, warning will be asserted by subject + // check. + if (!unwrapped.isFunctionType()) + return true; + + FunctionType::ExtInfo EI = unwrapped.get()->getExtInfo().withTIFlags(true); + type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI)); + return true; + } + if (attr.getKind() == ParsedAttr::AT_Regparm) { unsigned value; if (S.CheckRegparmAttr(attr, value)) diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index bf583b02f96b8d..e6ebd88807dbaf 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -47,6 +47,9 @@ serialization::TypeIdxFromBuiltin(const BuiltinType *BT) { case BuiltinType::ULong: ID = PREDEF_TYPE_ULONG_ID; break; + case BuiltinType::UInt48: + ID = PREDEF_TYPE_UINT48_ID; + break; case BuiltinType::ULongLong: ID = PREDEF_TYPE_ULONGLONG_ID; break; @@ -72,6 +75,9 @@ serialization::TypeIdxFromBuiltin(const BuiltinType *BT) { case BuiltinType::Long: ID = PREDEF_TYPE_LONG_ID; break; + case BuiltinType::Int48: + ID = PREDEF_TYPE_INT48_ID; + break; case BuiltinType::LongLong: ID = PREDEF_TYPE_LONGLONG_ID; break; diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 4dd2054d4b0798..2d65094e8fbe98 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -6833,6 +6833,9 @@ QualType ASTReader::GetType(TypeID ID) { case PREDEF_TYPE_ULONG_ID: T = Context.UnsignedLongTy; break; + case PREDEF_TYPE_UINT48_ID: + T = Context.UnsignedInt48Ty; + break; case PREDEF_TYPE_ULONGLONG_ID: T = Context.UnsignedLongLongTy; break; @@ -6854,6 +6857,9 @@ QualType ASTReader::GetType(TypeID ID) { case PREDEF_TYPE_LONG_ID: T = Context.LongTy; break; + case PREDEF_TYPE_INT48_ID: + T = Context.Int48Ty; + break; case PREDEF_TYPE_LONGLONG_ID: T = Context.LongLongTy; break; @@ -7342,6 +7348,12 @@ static Decl *getPredefinedDecl(ASTContext &Context, PredefinedDeclIDs ID) { case PREDEF_DECL_OBJC_PROTOCOL_ID: return Context.getObjCProtocolDecl(); + case PREDEF_DECL_INT_48_ID: + return Context.getInt48Decl(); + + case PREDEF_DECL_UNSIGNED_INT_48_ID: + return Context.getUInt48Decl(); + case PREDEF_DECL_INT_128_ID: return Context.getInt128Decl(); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 2a17360a67bc4c..1e8ac9f5756f76 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -529,6 +529,7 @@ void ASTWriter::WriteTypeAbbrevs() { Abv->Add(BitCodeAbbrevOp(0)); // NoCallerSavedRegs Abv->Add(BitCodeAbbrevOp(0)); // NoCfCheck Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // CmseNSCall + Abv->Add(BitCodeAbbrevOp(0)); // TIFlags // FunctionProtoType Abv->Add(BitCodeAbbrevOp(0)); // IsVariadic Abv->Add(BitCodeAbbrevOp(0)); // HasTrailingReturn @@ -4437,6 +4438,8 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, RegisterPredefDecl(Context.ObjCClassDecl, PREDEF_DECL_OBJC_CLASS_ID); RegisterPredefDecl(Context.ObjCProtocolClassDecl, PREDEF_DECL_OBJC_PROTOCOL_ID); + RegisterPredefDecl(Context.Int48Decl, PREDEF_DECL_INT_48_ID); + RegisterPredefDecl(Context.UInt48Decl, PREDEF_DECL_UNSIGNED_INT_48_ID); RegisterPredefDecl(Context.Int128Decl, PREDEF_DECL_INT_128_ID); RegisterPredefDecl(Context.UInt128Decl, PREDEF_DECL_UNSIGNED_INT_128_ID); RegisterPredefDecl(Context.ObjCInstanceTypeDecl, diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index fe36d49428a500..f9368deb956286 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -4690,6 +4690,7 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type, case clang::BuiltinType::Short: case clang::BuiltinType::Int: case clang::BuiltinType::Long: + case clang::BuiltinType::Int48: case clang::BuiltinType::LongLong: case clang::BuiltinType::Int128: return lldb::eEncodingSint; @@ -4703,6 +4704,7 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type, case clang::BuiltinType::UShort: case clang::BuiltinType::UInt: case clang::BuiltinType::ULong: + case clang::BuiltinType::UInt48: case clang::BuiltinType::ULongLong: case clang::BuiltinType::UInt128: return lldb::eEncodingUint; diff --git a/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h b/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h index 342537dcdc46f8..ef0e6b8865b60d 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h @@ -18,6 +18,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/CodeGen/CallingConvLower.h" #include "llvm/CodeGen/TargetCallingConv.h" +#include "llvm/IR/Attributes.h" #include "llvm/IR/CallingConv.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MachineValueType.h" @@ -79,6 +80,9 @@ class CallLowering { }; struct CallLoweringInfo { + /// Attributes attached to the call. + AttributeList CallAttributes; + /// Calling convention to be used for the call. CallingConv::ID CallConv = CallingConv::C; diff --git a/llvm/include/llvm/IR/CallingConv.h b/llvm/include/llvm/IR/CallingConv.h index c94490c85d8ec1..bd2ce496ce00b4 100644 --- a/llvm/include/llvm/IR/CallingConv.h +++ b/llvm/include/llvm/IR/CallingConv.h @@ -249,6 +249,7 @@ namespace CallingConv { Z80_LibCall_BC = 103, Z80_LibCall_L = 104, Z80_LibCall_F = 105, + Z80_TIFlags = 106, /// The highest possible calling convention ID. Must be some 2^k - 1. MaxID = 1023 diff --git a/llvm/include/llvm/MC/MCSectionOMF.h b/llvm/include/llvm/MC/MCSectionOMF.h index 2c486d7174bd72..e65289e55329d9 100644 --- a/llvm/include/llvm/MC/MCSectionOMF.h +++ b/llvm/include/llvm/MC/MCSectionOMF.h @@ -19,8 +19,6 @@ namespace llvm { class MCSectionOMF final : public MCSection { - SmallString<8> SectionName; - private: friend class MCContext; MCSectionOMF(StringRef Name, SectionKind K, MCSymbol *Begin); @@ -28,17 +26,13 @@ class MCSectionOMF final : public MCSection { public: ~MCSectionOMF(); - StringRef getSectionName() const { return SectionName; } - void PrintSwitchToSection(const MCAsmInfo &MAI, const Triple &T, raw_ostream &OS, const MCExpr *Subsection) const override; bool UseCodeAlign() const override { return getKind().isText(); } bool isVirtualSection() const override { return getKind().isBSS(); } - static bool classof(const MCSection *S) { - return S->getVariant() == SV_OMF; - } + static bool classof(const MCSection *S) { return S->getVariant() == SV_OMF; } }; } // end namespace llvm diff --git a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp index efab9a10459ba1..ea9e0697352b5f 100644 --- a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp +++ b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp @@ -64,6 +64,7 @@ bool CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, const CallBase &CB, setArgFlags(Info.OrigRet, AttributeList::ReturnIndex, DL, CB); MachineFunction &MF = MIRBuilder.getMF(); + Info.CallAttributes = CB.getAttributes(); Info.KnownCallees = CB.getMetadata(LLVMContext::MD_callees); Info.CallConv = CB.getCallingConv(); Info.SwiftErrorVReg = SwiftErrorVReg; diff --git a/llvm/lib/MC/MCSectionOMF.cpp b/llvm/lib/MC/MCSectionOMF.cpp index e36db1ac7ae006..8c7b5670bef85f 100644 --- a/llvm/lib/MC/MCSectionOMF.cpp +++ b/llvm/lib/MC/MCSectionOMF.cpp @@ -23,5 +23,5 @@ void MCSectionOMF::PrintSwitchToSection(const MCAsmInfo &MAI, const Triple &T, raw_ostream &OS, const MCExpr *Subsection) const { assert(!Subsection && "Unimplemented!"); - OS << "\tSEGMENT\t" << getSectionName() << '\n'; + OS << "\tSEGMENT\t" << getName() << '\n'; } diff --git a/llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp b/llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp index 96e4215f48d685..30fb32b500363a 100644 --- a/llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp +++ b/llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp @@ -71,6 +71,21 @@ struct OutgoingValueHandler : public CallLowering::ValueHandler { MIRBuilder.buildStore(extendRegister(ValVReg, VA), Addr, *MMO); } + bool finalize(CCState &State) override { + if (State.getCallingConv() == CallingConv::Z80_TIFlags) { + bool Is24Bit = STI.is24Bit(); + MVT VT = Is24Bit ? MVT::i24 : MVT::i16; + Register FlagsReg = + MIRBuilder + .buildConstant(LLT(VT), STI.hasEZ80Ops() ? 0xD00080 : 0x89F0) + .getReg(0); + CCValAssign VA = CCValAssign::getReg(~0, VT, Is24Bit ? Z80::UIY : Z80::IY, + VT, CCValAssign::Full); + assignValueToReg(FlagsReg, VA.getLocReg(), VA); + } + return ValueHandler::finalize(State); + } + protected: MachineInstrBuilder &MIB; const DataLayout &DL; diff --git a/llvm/lib/Target/Z80/Z80CallingConv.td b/llvm/lib/Target/Z80/Z80CallingConv.td index 5030333562d8ef..924ba8e8233fde 100644 --- a/llvm/lib/Target/Z80/Z80CallingConv.td +++ b/llvm/lib/Target/Z80/Z80CallingConv.td @@ -124,3 +124,5 @@ def CSR_Z80_C : CalleeSavedRegs<(add IX)>; def CSR_EZ80_C : CalleeSavedRegs<(add UIX)>; def CSR_Z80_AllRegs : CalleeSavedRegs<(add R16, A)>; def CSR_EZ80_AllRegs : CalleeSavedRegs<(add R24, A)>; +def CSR_Z80_TIFlags : CalleeSavedRegs<(add I16)>; +def CSR_EZ80_TIFlags : CalleeSavedRegs<(add I24)>; diff --git a/llvm/lib/Target/Z80/Z80RegisterInfo.cpp b/llvm/lib/Target/Z80/Z80RegisterInfo.cpp index 4eab54d6717d62..1127c6455169a1 100644 --- a/llvm/lib/Target/Z80/Z80RegisterInfo.cpp +++ b/llvm/lib/Target/Z80/Z80RegisterInfo.cpp @@ -102,6 +102,8 @@ Z80RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { case CallingConv::Z80_LibCall_L: case CallingConv::Z80_LibCall_F: return Is24Bit ? CSR_EZ80_AllRegs_SaveList : CSR_Z80_AllRegs_SaveList; + case CallingConv::Z80_TIFlags: + return Is24Bit ? CSR_EZ80_TIFlags_SaveList : CSR_Z80_TIFlags_SaveList; } } @@ -121,6 +123,8 @@ Z80RegisterInfo::getCallPreservedMask(const MachineFunction &MF, case CallingConv::Z80_LibCall_L: case CallingConv::Z80_LibCall_F: return Is24Bit ? CSR_EZ80_AllRegs_RegMask : CSR_Z80_AllRegs_RegMask; + case CallingConv::Z80_TIFlags: + return Is24Bit ? CSR_EZ80_TIFlags_RegMask : CSR_Z80_TIFlags_RegMask; } } const uint32_t *Z80RegisterInfo::getNoPreservedMask() const { From 418a0eddfcaaaea0dec5b797e39ad3a8682c8270 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 28 Nov 2019 15:48:37 -0500 Subject: [PATCH 15/21] [Z80] Add Z80 target-specific optimizations. --- .../llvm/CodeGen/GlobalISel/CombinerHelper.h | 75 ++- .../llvm/CodeGen/GlobalISel/MIPatternMatch.h | 26 +- llvm/include/llvm/CodeGen/LiveRegUnits.h | 5 + .../include/llvm/Target/GlobalISel/Combine.td | 75 +++ .../lib/CodeGen/GlobalISel/CombinerHelper.cpp | 432 +++++++++++- .../CodeGen/GlobalISel/LegalizerHelper.cpp | 3 + llvm/lib/CodeGen/LiveRegUnits.cpp | 18 + llvm/lib/Target/Z80/CMakeLists.txt | 6 +- llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp | 395 ++++++++++- llvm/lib/Target/Z80/GISel/Z80CallLowering.h | 18 + .../Z80/GISel/Z80InstructionSelector.cpp | 631 +++++++++++++----- .../lib/Target/Z80/GISel/Z80LegalizerInfo.cpp | 45 +- llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.h | 2 + .../Z80/GISel/Z80PreLegalizerCombiner.cpp | 139 ++++ llvm/lib/Target/Z80/Z80.h | 4 + llvm/lib/Target/Z80/Z80.td | 1 + llvm/lib/Target/Z80/Z80Combine.td | 22 + llvm/lib/Target/Z80/Z80FrameLowering.cpp | 82 ++- llvm/lib/Target/Z80/Z80FrameLowering.h | 1 + llvm/lib/Target/Z80/Z80InstrInfo.cpp | 157 ++++- llvm/lib/Target/Z80/Z80InstrInfo.h | 34 +- llvm/lib/Target/Z80/Z80InstrInfo.td | 35 +- llvm/lib/Target/Z80/Z80MachineFunctionInfo.h | 17 + .../Target/Z80/Z80MachineLateOptimization.cpp | 286 ++++++++ llvm/lib/Target/Z80/Z80PostSelectCombiner.cpp | 435 ++++++++++++ llvm/lib/Target/Z80/Z80RegisterInfo.cpp | 17 +- llvm/lib/Target/Z80/Z80RegisterInfo.td | 2 +- llvm/lib/Target/Z80/Z80TargetMachine.cpp | 26 + 28 files changed, 2709 insertions(+), 280 deletions(-) create mode 100644 llvm/lib/Target/Z80/GISel/Z80PreLegalizerCombiner.cpp create mode 100644 llvm/lib/Target/Z80/Z80Combine.td create mode 100644 llvm/lib/Target/Z80/Z80MachineLateOptimization.cpp create mode 100644 llvm/lib/Target/Z80/Z80PostSelectCombiner.cpp diff --git a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h index 43a8cb2a1d51c8..397237b95299c2 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h @@ -24,13 +24,15 @@ namespace llvm { class GISelChangeObserver; +class GISelKnownBits; +class GlobalValue; +class LegalizerInfo; +class MachineBasicBlock; +class MachineDominatorTree; class MachineIRBuilder; -class MachineRegisterInfo; class MachineInstr; class MachineOperand; -class GISelKnownBits; -class MachineDominatorTree; -class LegalizerInfo; +class MachineRegisterInfo; struct PreferredTuple { LLT Ty; // The result type of the extend. @@ -47,7 +49,22 @@ struct IndexedLoadStoreMatchInfo { struct PtrAddChain { int64_t Imm; - Register Base; + Register Reg; +}; + +struct PtrAddGlobal { + int64_t Imm; + const GlobalValue *Global; +}; + +struct PtrAddConst { + int64_t Imm; + LLT Ty; +}; + +struct FunnelShift { + Register ShiftLeftReg, ShiftRightReg; + int64_t ShiftLeftAmt; }; class CombinerHelper { @@ -95,6 +112,19 @@ class CombinerHelper { /// a single basic block. bool dominates(const MachineInstr &DefMI, const MachineInstr &UseMI); + /// Returns true if \p DefMBB dominates \p UseMBB. By definition a block + /// dominates itself. + /// + /// If we haven't been provided with a MachineDominatorTree during + /// construction, this function returns a conservative result that just checks + /// for equality. + bool dominates(MachineBasicBlock &DefMBB, MachineBasicBlock &UseMBB); + + /// Checks if MI can be moved to the beginning of MBB. + /// + /// \returns true if the instruction can be moved. + bool canMove(MachineInstr &MI, MachineBasicBlock &MBB, bool &SawStore); + /// If \p MI is extend that consumes the result of a load, try to combine it. /// Returns true if MI changed. bool tryCombineExtendingLoads(MachineInstr &MI); @@ -249,6 +279,41 @@ class CombinerHelper { bool applySimplifyAddToSub(MachineInstr &MI, std::tuple &MatchInfo); + bool matchPtrAddGlobalImmed(MachineInstr &MI, PtrAddGlobal &MatchInfo); + bool applyPtrAddGlobalImmed(MachineInstr &MI, PtrAddGlobal &MatchInfo); + + bool matchPtrAddConstImmed(MachineInstr &MI, PtrAddConst &MatchInfo); + bool applyPtrAddConstImmed(MachineInstr &MI, PtrAddConst &MatchInfo); + + bool matchCombineShlToAdd(MachineInstr &MI, unsigned &ShiftVal); + bool applyCombineShlToAdd(MachineInstr &MI, unsigned &ShiftVal); + + bool matchCombineSExtToZExt(MachineInstr &MI); + bool applyCombineSExtToZExt(MachineInstr &MI); + + bool matchCombineOrToAdd(MachineInstr &MI); + bool applyCombineOrToAdd(MachineInstr &MI); + + bool matchCombineFunnelShift(MachineInstr &MI, FunnelShift &MatchInfo); + void applyCombineFunnelShift(MachineInstr &MI, const FunnelShift &MatchInfo); + + bool matchCombineIdentity(MachineInstr &MI); + bool applyCombineIdentity(MachineInstr &MI); + + /// Split branches on conditions combined with and/or into multiple branches. + bool matchSplitConditions(MachineInstr &MI); + void applySplitConditions(MachineInstr &MI); + + bool matchFlipCondition(MachineInstr &MI, MachineInstr *&CmpI); + void applyFlipCondition(MachineInstr &MI, MachineInstr &CmpI); + + /// Undo combines involving popcnt. + bool matchLowerIsPowerOfTwo(MachineInstr &MI); + void applyLowerIsPowerOfTwo(MachineInstr &MI); + + bool matchSinkConstant(MachineInstr &MI, MachineInstr *&DomUseMI); + void applySinkConstant(MachineInstr &MI, MachineInstr &DomUseMI); + /// Try to transform \p MI by using all of the above /// combine functions. Returns true if changed. bool tryCombine(MachineInstr &MI); diff --git a/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h b/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h index 043be086ff417d..879c7a2920e446 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h @@ -39,9 +39,12 @@ inline OneUse_match m_OneUse(const SubPat &SP) { return SP; } +template struct ConstantMatch { - int64_t &CR; - ConstantMatch(int64_t &C) : CR(C) {} + static_assert(std::numeric_limits::is_integer, + "Only integral types are allowed."); + Int &CR; + ConstantMatch(Int &C) : CR(C) {} bool match(const MachineRegisterInfo &MRI, Register Reg) { if (auto MaybeCst = getConstantVRegVal(Reg, MRI)) { CR = *MaybeCst; @@ -51,7 +54,8 @@ struct ConstantMatch { } }; -inline ConstantMatch m_ICst(int64_t &Cst) { return ConstantMatch(Cst); } +template +inline ConstantMatch m_ICst(Int &Cst) { return {Cst}; } // TODO: Rework this for different kinds of MachineOperand. // Currently assumes the Src for a match is a register. @@ -234,11 +238,17 @@ m_GAnd(const LHS &L, const RHS &R) { } template -inline BinaryOp_match m_GOr(const LHS &L, - const RHS &R) { +inline BinaryOp_match +m_GOr(const LHS &L, const RHS &R) { return BinaryOp_match(L, R); } +template +inline BinaryOp_match +m_GXor(const LHS &L, const RHS &R) { + return BinaryOp_match(L, R); +} + template inline BinaryOp_match m_GShl(const LHS &L, const RHS &R) { @@ -251,6 +261,12 @@ m_GLShr(const LHS &L, const RHS &R) { return BinaryOp_match(L, R); } +template +inline BinaryOp_match +m_GAShr(const LHS &L, const RHS &R) { + return BinaryOp_match(L, R); +} + // Helper for unary instructions (G_[ZSA]EXT/G_TRUNC) etc template struct UnaryOp_match { SrcTy L; diff --git a/llvm/include/llvm/CodeGen/LiveRegUnits.h b/llvm/include/llvm/CodeGen/LiveRegUnits.h index 1ed091e3bb5e9d..32a8a55cf6989b 100644 --- a/llvm/include/llvm/CodeGen/LiveRegUnits.h +++ b/llvm/include/llvm/CodeGen/LiveRegUnits.h @@ -122,6 +122,11 @@ class LiveRegUnits { return true; } + /// Updates liveness when stepping forwards over the instruction \p MI. + /// This removes the units killed in \p MI and then adds all live units + /// defined in \p MI. + void stepForward(const MachineInstr &MI); + /// Updates liveness when stepping backwards over the instruction \p MI. /// This removes all register units defined or clobbered in \p MI and then /// adds the units used (as in use operands) in \p MI. diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td index eeb2761faeb9f1..86e46d2e46fea3 100644 --- a/llvm/include/llvm/Target/GlobalISel/Combine.td +++ b/llvm/include/llvm/Target/GlobalISel/Combine.td @@ -264,6 +264,81 @@ def identity_combines : GICombineGroup<[select_same_val, right_identity_zero, binop_right_to_zero]>; def trivial_combines : GICombineGroup<[copy_prop, mul_to_shl]>; + +def ptr_add_global_matchdata : GIDefMatchData<"PtrAddGlobal">; +def ptr_add_global_immed : GICombineRule< + (defs root:$mi, ptr_add_global_matchdata:$matchinfo), + (match (wip_match_opcode G_PTR_ADD):$mi, + [{ return Helper.matchPtrAddGlobalImmed(*${mi}, ${matchinfo}); }]), + (apply [{ Helper.applyPtrAddGlobalImmed(*${mi}, ${matchinfo}); }])>; + +def ptr_add_const_matchdata : GIDefMatchData<"PtrAddConst">; +def ptr_add_const_immed : GICombineRule< + (defs root:$mi, ptr_add_const_matchdata:$matchinfo), + (match (wip_match_opcode G_PTR_ADD):$mi, + [{ return Helper.matchPtrAddConstImmed(*${mi}, ${matchinfo}); }]), + (apply [{ Helper.applyPtrAddConstImmed(*${mi}, ${matchinfo}); }])>; + +def shl_to_add_matchdata : GIDefMatchData<"unsigned">; +def shl_to_add : GICombineRule< + (defs root:$mi, shl_to_add_matchdata:$matchinfo), + (match (wip_match_opcode G_SHL):$mi, + [{ return Helper.matchCombineShlToAdd(*${mi}, ${matchinfo}); }]), + (apply [{ Helper.applyCombineShlToAdd(*${mi}, ${matchinfo}); }])>; + +def sext_to_zext : GICombineRule< + (defs root:$mi), + (match (wip_match_opcode G_SEXT):$mi, + [{ return Helper.matchCombineSExtToZExt(*${mi}); }]), + (apply [{ Helper.applyCombineSExtToZExt(*${mi}); }])>; + +def or_to_add : GICombineRule< + (defs root:$mi), + (match (wip_match_opcode G_OR):$mi, + [{ return Helper.matchCombineOrToAdd(*${mi}); }]), + (apply [{ Helper.applyCombineOrToAdd(*${mi}); }])>; + +def funnel_shift_matchdata : GIDefMatchData<"FunnelShift">; +def funnel_shift : GICombineRule< + (defs root:$mi, funnel_shift_matchdata:$matchinfo), + (match (wip_match_opcode G_ADD):$mi, + [{ return Helper.matchCombineFunnelShift(*${mi}, ${matchinfo}); }]), + (apply [{ Helper.applyCombineFunnelShift(*${mi}, ${matchinfo}); }])>; + +def combine_identity : GICombineRule< + (defs root:$mi), + (match (wip_match_opcode G_ADD, G_SUB, G_MUL, G_SDIV, G_UDIV, + G_AND, G_OR, G_XOR, G_SHL, G_LSHR, G_ASHR, + G_PTR_ADD, G_PTRMASK):$mi, + [{ return Helper.matchCombineIdentity(*${mi}); }]), + (apply [{ Helper.applyCombineIdentity(*${mi}); }])>; + +def split_conditions : GICombineRule< + (defs root:$mi), + (match (wip_match_opcode G_BRCOND):$mi, + [{ return Helper.matchSplitConditions(*${mi}); }]), + (apply [{ Helper.applySplitConditions(*${mi}); }])>; + +def flip_condition_matchdata : GIDefMatchData<"MachineInstr *">; +def flip_condition : GICombineRule< + (defs root:$mi, flip_condition_matchdata:$matchinfo), + (match (wip_match_opcode G_XOR):$mi, + [{ return Helper.matchFlipCondition(*${mi}, ${matchinfo}); }]), + (apply [{ Helper.applyFlipCondition(*${mi}, *${matchinfo}); }])>; + +def lower_is_power_of_two : GICombineRule< + (defs root:$mi), + (match (wip_match_opcode G_ICMP):$mi, + [{ return Helper.matchLowerIsPowerOfTwo(*${mi}); }]), + (apply [{ Helper.applyLowerIsPowerOfTwo(*${mi}); }])>; + +def sink_const_matchdata : GIDefMatchData<"MachineInstr *">; +def sink_const : GICombineRule< + (defs root:$mi, sink_const_matchdata:$matchinfo), + (match (wip_match_opcode G_CONSTANT):$mi, + [{ return Helper.matchSinkConstant(*${mi}, ${matchinfo}); }]), + (apply [{ Helper.applySinkConstant(*${mi}, *${matchinfo}); }])>; + def all_combines : GICombineGroup<[trivial_combines, ptr_add_immed_chain, combines_for_extload, combine_indexed_load_store, undef_combines, identity_combines, simplify_add_to_sub]>; diff --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp index 32bad28d318ba9..c84424178a1728 100644 --- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp +++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp @@ -576,6 +576,37 @@ bool CombinerHelper::dominates(const MachineInstr &DefMI, return isPredecessor(DefMI, UseMI); } +bool CombinerHelper::dominates(MachineBasicBlock &DefMBB, + MachineBasicBlock &UseMBB) { + if (MDT) + return MDT->dominates(&DefMBB, &UseMBB); + return &DefMBB == &UseMBB; +} + +bool CombinerHelper::canMove(MachineInstr &MI, MachineBasicBlock &MBB, + bool &SawStore) { + if (MI.isConvergent() || !MI.isSafeToMove(nullptr, SawStore)) + return false; + for (auto &MO : MI.operands()) { + if (!MO.isReg()) + continue; + Register Reg = MO.getReg(); + if (!Reg.isVirtual()) + return false; + if (!MO.isDef() || MO.isImplicit()) + continue; + for (auto &UseMO : MRI.use_nodbg_operands(Reg)) { + auto &UseMI = *UseMO.getParent(); + auto *UseMBB = UseMI.getParent(); + if (UseMI.isPHI()) + UseMBB = UseMI.getOperand(UseMI.getOperandNo(&UseMO) + 1).getMBB(); + if (!dominates(MBB, *UseMBB)) + return false; + } + } + return true; +} + bool CombinerHelper::findPostIndexCandidate(MachineInstr &MI, Register &Addr, Register &Base, Register &Offset) { auto &MF = *MI.getParent()->getParent(); @@ -1360,18 +1391,18 @@ bool CombinerHelper::matchPtrAddImmedChain(MachineInstr &MI, // Pass the combined immediate to the apply function. MatchInfo.Imm = MaybeImmVal->Value + MaybeImm2Val->Value; - MatchInfo.Base = Base; + MatchInfo.Reg = Base; return true; } bool CombinerHelper::applyPtrAddImmedChain(MachineInstr &MI, PtrAddChain &MatchInfo) { assert(MI.getOpcode() == TargetOpcode::G_PTR_ADD && "Expected G_PTR_ADD"); - MachineIRBuilder MIB(MI); + Builder.setInstr(MI); LLT OffsetTy = MRI.getType(MI.getOperand(2).getReg()); - auto NewOffset = MIB.buildConstant(OffsetTy, MatchInfo.Imm); + auto NewOffset = Builder.buildConstant(OffsetTy, MatchInfo.Imm); Observer.changingInstr(MI); - MI.getOperand(1).setReg(MatchInfo.Base); + MI.getOperand(1).setReg(MatchInfo.Reg); MI.getOperand(2).setReg(NewOffset.getReg(0)); Observer.changedInstr(MI); return true; @@ -1391,11 +1422,11 @@ bool CombinerHelper::matchCombineMulToShl(MachineInstr &MI, bool CombinerHelper::applyCombineMulToShl(MachineInstr &MI, unsigned &ShiftVal) { assert(MI.getOpcode() == TargetOpcode::G_MUL && "Expected a G_MUL"); - MachineIRBuilder MIB(MI); + Builder.setInstr(MI); LLT ShiftTy = MRI.getType(MI.getOperand(0).getReg()); - auto ShiftCst = MIB.buildConstant(ShiftTy, ShiftVal); + auto ShiftCst = Builder.buildConstant(ShiftTy, ShiftVal); Observer.changingInstr(MI); - MI.setDesc(MIB.getTII().get(TargetOpcode::G_SHL)); + MI.setDesc(Builder.getTII().get(TargetOpcode::G_SHL)); MI.getOperand(2).setReg(ShiftCst.getReg(0)); Observer.changedInstr(MI); return true; @@ -1709,6 +1740,393 @@ bool CombinerHelper::applySimplifyAddToSub( return true; } +bool CombinerHelper::matchPtrAddGlobalImmed(MachineInstr &MI, + PtrAddGlobal &MatchInfo) { + // We're trying to match the following pattern: + // %t1 = G_GLOBAL_VALUE @global+offset + // %root = G_PTR_ADD %t1, G_CONSTANT imm + // --> + // %root = G_GLOBAL_VALUE @global+offset+imm + + if (MI.getOpcode() != TargetOpcode::G_PTR_ADD) + return false; + + Register Global = MI.getOperand(1).getReg(); + Register Imm = MI.getOperand(2).getReg(); + auto MaybeImmVal = getConstantVRegValWithLookThrough(Imm, MRI); + if (!MaybeImmVal) + return false; + + MachineInstr *GlobalDef = MRI.getUniqueVRegDef(Global); + if (!GlobalDef || GlobalDef->getOpcode() != TargetOpcode::G_GLOBAL_VALUE) + return false; + + const GlobalValue *Base = GlobalDef->getOperand(1).getGlobal(); + auto Offset = GlobalDef->getOperand(1).getOffset(); + + // Pass the combined immediate to the apply function. + MatchInfo.Imm = MaybeImmVal->Value + Offset; + MatchInfo.Global = Base; + return true; +} + +bool CombinerHelper::applyPtrAddGlobalImmed(MachineInstr &MI, + PtrAddGlobal &MatchInfo) { + assert(MI.getOpcode() == TargetOpcode::G_PTR_ADD && "Expected G_PTR_ADD"); + Builder.setInstr(MI); + Observer.changingInstr(MI); + MI.setDesc(Builder.getTII().get(TargetOpcode::G_GLOBAL_VALUE)); + MI.getOperand(1).ChangeToGA(MatchInfo.Global, MatchInfo.Imm); + MI.RemoveOperand(2); + Observer.changedInstr(MI); + return true; +} + +bool CombinerHelper::matchPtrAddConstImmed(MachineInstr &MI, + PtrAddConst &MatchInfo) { + // We're trying to match the following pattern: + // %t1 = G_INTTOPTR G_CONSTANT imm2 + // %root = G_PTR_ADD %t1, G_CONSTANT imm + // --> + // %root = G_INTTOPTR G_CONSTANT imm+imm2 + + if (MI.getOpcode() != TargetOpcode::G_PTR_ADD) + return false; + + Register IntToPtr = MI.getOperand(1).getReg(); + Register Imm = MI.getOperand(2).getReg(); + auto MaybeImmVal = getConstantVRegValWithLookThrough(Imm, MRI); + if (!MaybeImmVal) + return false; + + MachineInstr *IntToPtrDef = MRI.getUniqueVRegDef(IntToPtr); + if (!IntToPtrDef || IntToPtrDef->getOpcode() != TargetOpcode::G_INTTOPTR) + return false; + + Register Imm2 = IntToPtrDef->getOperand(1).getReg(); + auto MaybeImm2Val = getConstantVRegValWithLookThrough(Imm2, MRI); + if (!MaybeImm2Val) + return false; + + // Pass the combined immediate to the apply function. + MatchInfo.Imm = MaybeImmVal->Value + MaybeImm2Val->Value; + MatchInfo.Ty = MRI.getType(Imm2); + return true; +} + +bool CombinerHelper::applyPtrAddConstImmed(MachineInstr &MI, + PtrAddConst &MatchInfo) { + assert(MI.getOpcode() == TargetOpcode::G_PTR_ADD && "Expected G_PTR_ADD"); + Builder.setInstr(MI); + auto NewConst = Builder.buildConstant(MatchInfo.Ty, MatchInfo.Imm); + Observer.changingInstr(MI); + MI.setDesc(Builder.getTII().get(TargetOpcode::G_INTTOPTR)); + MI.getOperand(1).setReg(NewConst.getReg(0)); + MI.RemoveOperand(2); + Observer.changedInstr(MI); + return true; +} + +bool CombinerHelper::matchCombineShlToAdd(MachineInstr &MI, + unsigned &ShiftVal) { + if (MI.getOpcode() != TargetOpcode::G_SHL) + return false; + Register RHSReg = MI.getOperand(2).getReg(); + auto RHSImm = getConstantVRegValWithLookThrough(RHSReg, MRI); + if (!RHSImm || !RHSImm->Value) + return false; + ShiftVal = RHSImm->Value; + return true; +} + +bool CombinerHelper::applyCombineShlToAdd(MachineInstr &MI, + unsigned &ShiftVal) { + assert(MI.getOpcode() == TargetOpcode::G_SHL && "Expected a G_SHL"); + Register Reg = MI.getOperand(1).getReg(); + LLT RegTy = MRI.getType(Reg); + Builder.setInstr(MI); + while (--ShiftVal) + Reg = Builder.buildAdd(RegTy, Reg, Reg).getReg(0); + Observer.changingInstr(MI); + MI.setDesc(Builder.getTII().get(TargetOpcode::G_ADD)); + MI.getOperand(1).setReg(Reg); + MI.getOperand(2).setReg(Reg); + Observer.changedInstr(MI); + return true; +} + +bool CombinerHelper::matchCombineSExtToZExt(MachineInstr &MI) { + if (MI.getOpcode() != TargetOpcode::G_SEXT) + return false; + return KB->getKnownBits(MI.getOperand(1).getReg()).isNonNegative(); +} + +bool CombinerHelper::applyCombineSExtToZExt(MachineInstr &MI) { + assert(MI.getOpcode() == TargetOpcode::G_SEXT && "Expected a G_SEXT"); + Builder.setInstr(MI); + Observer.changingInstr(MI); + MI.setDesc(Builder.getTII().get(TargetOpcode::G_ZEXT)); + Observer.changedInstr(MI); + return true; +} + +bool CombinerHelper::matchCombineOrToAdd(MachineInstr &MI) { + if (MI.getOpcode() != TargetOpcode::G_OR) + return false; + APInt Zeroes = KB->getKnownZeroes(MI.getOperand(1).getReg()); + Zeroes |= KB->getKnownZeroes(MI.getOperand(2).getReg()); + return Zeroes.isAllOnesValue(); +} + +bool CombinerHelper::applyCombineOrToAdd(MachineInstr &MI) { + assert(MI.getOpcode() == TargetOpcode::G_OR && "Expected a G_OR"); + Builder.setInstr(MI); + Observer.changingInstr(MI); + MI.setDesc(Builder.getTII().get(TargetOpcode::G_ADD)); + Observer.changedInstr(MI); + return true; +} + +bool CombinerHelper::matchCombineFunnelShift(MachineInstr &MI, + FunnelShift &MatchInfo) { + Register DstReg = MI.getOperand(0).getReg(); + int64_t ShiftRightAmt; + return mi_match(DstReg, MRI, + m_GAdd(m_GShl(m_Reg(MatchInfo.ShiftLeftReg), + m_ICst(MatchInfo.ShiftLeftAmt)), + m_GLShr(m_Reg(MatchInfo.ShiftRightReg), + m_ICst(ShiftRightAmt)))) && + MatchInfo.ShiftLeftAmt + ShiftRightAmt == + MRI.getType(DstReg).getSizeInBits(); +} + +void CombinerHelper::applyCombineFunnelShift(MachineInstr &MI, + const FunnelShift &MatchInfo) { + MachineIRBuilder MIB(MI); + Register DstReg = MI.getOperand(0).getReg(); + auto AmtI = MIB.buildConstant(MRI.getType(DstReg), MatchInfo.ShiftLeftAmt); + MIB.buildInstr( + TargetOpcode::G_FSHL, {DstReg}, + {MatchInfo.ShiftLeftReg, MatchInfo.ShiftRightReg, AmtI.getReg(0)}); + MI.eraseFromParent(); +} + +bool CombinerHelper::matchCombineIdentity(MachineInstr &MI) { + int64_t IdentityElement; + switch (MI.getOpcode()) { + case TargetOpcode::G_ADD: + case TargetOpcode::G_SUB: + case TargetOpcode::G_OR: + case TargetOpcode::G_XOR: + case TargetOpcode::G_SHL: + case TargetOpcode::G_LSHR: + case TargetOpcode::G_ASHR: + case TargetOpcode::G_PTR_ADD: + IdentityElement = 0; + break; + case TargetOpcode::G_MUL: + case TargetOpcode::G_SDIV: + case TargetOpcode::G_UDIV: + IdentityElement = 1; + break; + case TargetOpcode::G_AND: + case TargetOpcode::G_PTRMASK: + IdentityElement = -1; + break; + default: + return false; + } + auto MaybeImmVal = + getConstantVRegValWithLookThrough(MI.getOperand(2).getReg(), MRI); + return MaybeImmVal && MaybeImmVal->Value == IdentityElement; +} + +bool CombinerHelper::applyCombineIdentity(MachineInstr &MI) { + Builder.setInstr(MI); + Observer.changingInstr(MI); + MI.setDesc(Builder.getTII().get(TargetOpcode::COPY)); + MI.RemoveOperand(2); + Observer.changedInstr(MI); + return true; +} + +bool CombinerHelper::matchSplitConditions(MachineInstr &MI) { + if (MI.getOpcode() != TargetOpcode::G_BRCOND) + return false; + Register CondReg = MI.getOperand(0).getReg(); + MachineInstr *CondMI = MRI.getVRegDef(CondReg); + return MRI.hasOneUse(CondReg) && CondMI && + (CondMI->getOpcode() == TargetOpcode::G_AND || + CondMI->getOpcode() == TargetOpcode::G_OR); +} + +void CombinerHelper::applySplitConditions(MachineInstr &MI) { + MachineInstr &CondMI = *MRI.getVRegDef(MI.getOperand(0).getReg()); + bool IsAnd = CondMI.getOpcode() == TargetOpcode::G_AND; + Register CondLHSReg = CondMI.getOperand(1).getReg(); + Register CondRHSReg = CondMI.getOperand(2).getReg(); + CondMI.eraseFromParent(); + + MachineBasicBlock::iterator II(MI); + MachineBasicBlock &CurMBB = *MI.getParent(); + MachineFunction &MF = *CurMBB.getParent(); + Builder.setMBB(CurMBB); + auto NextMBB = std::next(CurMBB.getIterator()); + + auto &NewMBB = *MF.CreateMachineBasicBlock(CurMBB.getBasicBlock()); + MF.insert(NextMBB, &NewMBB); + NewMBB.splice(NewMBB.begin(), &CurMBB, II, CurMBB.end()); + NewMBB.transferSuccessorsAndUpdatePHIs(&CurMBB); + + MachineBasicBlock *CommonMBB = MI.getOperand(1).getMBB(), *SuccMBB; + MachineInstr &Term = NewMBB.back(); + if (II != Term) { + assert(std::next(II) == Term && Term.getOpcode() == TargetOpcode::G_BR && + "Expected unconditional branch or nothing after conditional one."); + SuccMBB = Term.getOperand(0).getMBB(); + } else + SuccMBB = &*NextMBB; + if (IsAnd) { + std::swap(CommonMBB, SuccMBB); + II = Builder.buildBrCond(CondLHSReg, NewMBB); + Builder.buildBr(*CommonMBB); + } else + II = Builder.buildBrCond(CondLHSReg, *CommonMBB); + + CurMBB.addSuccessor(&NewMBB); + CurMBB.addSuccessor(CommonMBB); + if (MDT) { + if (SuccMBB->pred_size() == 1) { + MDT->addNewBlock(&NewMBB, &CurMBB); + MDT->changeImmediateDominator(SuccMBB, &NewMBB); + } else + MDT->recordSplitCriticalEdge(&CurMBB, SuccMBB, &NewMBB); + } + for (MachineInstr &PhiMI : CommonMBB->phis()) { + for (unsigned I = 1, E = PhiMI.getNumOperands(); I != E; I += 2) { + if (PhiMI.getOperand(I + 1).getMBB() == &NewMBB) { + MachineInstrBuilder(MF, PhiMI).add(PhiMI.getOperand(I)).addMBB(&CurMBB); + break; + } + } + } + MI.getOperand(0).setReg(CondRHSReg); + + bool SawStore = false; + while (II != CurMBB.begin()) { + auto PrevII = II; + --PrevII; + if (canMove(*PrevII, NewMBB, SawStore)) + NewMBB.splice(NewMBB.begin(), &CurMBB, PrevII); + else + II = PrevII; + } +} + +bool CombinerHelper::matchFlipCondition(MachineInstr &MI, MachineInstr *&CmpI) { + Register DstReg = MI.getOperand(0).getReg(); + int64_t Cst; + return mi_match(DstReg, MRI, m_GXor(m_MInstr(CmpI), m_ICst(Cst))) && + (CmpI->getOpcode() == TargetOpcode::G_ICMP || + CmpI->getOpcode() == TargetOpcode::G_FCMP) && Cst; +} + +void CombinerHelper::applyFlipCondition(MachineInstr &MI, MachineInstr &CmpI) { + Builder.setInsertPt(*MI.getParent(), MI); + Builder.buildInstr(CmpI.getOpcode(), {MI.getOperand(0)}, + {CmpInst::getInversePredicate(CmpInst::Predicate( + CmpI.getOperand(1).getPredicate())), + CmpI.getOperand(2), CmpI.getOperand(3)}); + MI.eraseFromParent(); +} + +bool CombinerHelper::matchLowerIsPowerOfTwo(MachineInstr &MI) { + if (MI.getOpcode() != TargetOpcode::G_ICMP) + return false; + auto Pred = CmpInst::Predicate(MI.getOperand(1).getPredicate()); + MachineInstr *CtPop = MRI.getVRegDef(MI.getOperand(2).getReg()); + Register RHSReg = MI.getOperand(3).getReg(); + auto RHSConst = getConstantVRegVal(RHSReg, MRI); + if (!CtPop || CtPop->getOpcode() != TargetOpcode::G_CTPOP || !RHSConst) + return false; + if (((Pred == CmpInst::ICMP_EQ || Pred == CmpInst::ICMP_NE) && + *RHSConst == 1) || + (Pred == CmpInst::ICMP_ULT && *RHSConst == 2) || + (Pred == CmpInst::ICMP_UGT && *RHSConst == 1)) + return true; + return false; +} + +void CombinerHelper::applyLowerIsPowerOfTwo(MachineInstr &MI) { + Builder.setInsertPt(*MI.getParent(), MI); + Register ResReg = MI.getOperand(0).getReg(); + auto Pred = CmpInst::Predicate(MI.getOperand(1).getPredicate()); + MachineInstr *CtPop = MRI.getVRegDef(MI.getOperand(2).getReg()); + Register SrcReg = CtPop->getOperand(1).getReg(); + LLT Ty = MRI.getType(SrcReg); + auto NegOne = Builder.buildConstant(Ty, -1); + auto Add = Builder.buildAdd(Ty, SrcReg, NegOne); + auto And = Builder.buildAnd(Ty, SrcReg, Add); + auto Zero = Builder.buildConstant(Ty, 0); + if (Pred == CmpInst::ICMP_EQ || Pred == CmpInst::ICMP_NE) { + auto IsNotZero = Builder.buildICmp(CmpInst::getInversePredicate(Pred), + LLT::scalar(1), SrcReg, Zero); + auto IsPowerOfTwo = Builder.buildICmp(Pred, LLT::scalar(1), And, Zero); + Builder.buildInstr(Pred == CmpInst::ICMP_EQ ? TargetOpcode::G_AND + : TargetOpcode::G_OR, + {ResReg}, {IsNotZero, IsPowerOfTwo}); + } else + Builder.buildICmp(Pred == CmpInst::ICMP_ULT ? CmpInst::ICMP_EQ + : CmpInst::ICMP_NE, + ResReg, And, Zero); + MI.eraseFromParent(); +} + +bool CombinerHelper::matchSinkConstant(MachineInstr &MI, + MachineInstr *&DomUseMI) { + if (MI.getOpcode() != TargetOpcode::G_CONSTANT) + return false; + Register Reg = MI.getOperand(0).getReg(); + SmallVector DomMIs; + for (MachineInstr &UseMI : MRI.use_instructions(Reg)) { + if (UseMI.isPHI()) + return false; + bool Dominated = false; + for (unsigned I = 0; I != DomMIs.size();) { + MachineInstr &DomMI = *DomMIs[I]; + if (dominates(DomMI, UseMI)) { + Dominated = true; + break; + } + if (dominates(UseMI, DomMI)) { + if (I != DomMIs.size() - 1) + DomMIs[I] = DomMIs.pop_back_val(); + else + DomMIs.pop_back(); + continue; + } + ++I; + } + if (!Dominated) + DomMIs.push_back(&UseMI); + } + if (DomMIs.size() != 1) + return false; + DomUseMI = DomMIs[0]; + if (MI.getParent() != DomUseMI->getParent()) + return true; + for (MachineBasicBlock::iterator I(MI), E(DomUseMI); I != E; ++I) + if (I->getOpcode() != TargetOpcode::G_CONSTANT) + return true; + return false; +} + +void CombinerHelper::applySinkConstant(MachineInstr &MI, + MachineInstr &DomUseMI) { + MI.removeFromParent(); + DomUseMI.getParent()->insert(DomUseMI, &MI); +} + bool CombinerHelper::tryCombine(MachineInstr &MI) { if (tryCombineCopy(MI)) return true; diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp index c4405cf6462978..cb78b0d78ff943 100644 --- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp +++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp @@ -483,6 +483,8 @@ static RTLIB::Libcall getRTLibDesc(unsigned Opcode, unsigned Size) { RTLIBCASE(RINT_F); case TargetOpcode::G_FNEARBYINT: RTLIBCASE(NEARBYINT_F); + case TargetOpcode::G_FCOPYSIGN: + RTLIBCASE(COPYSIGN_F); case TargetOpcode::G_FNEG: RTLIBCASE(NEG_F); case TargetOpcode::G_FABS: @@ -733,6 +735,7 @@ LegalizerHelper::libcall(MachineInstr &MI) { case TargetOpcode::G_FSQRT: case TargetOpcode::G_FRINT: case TargetOpcode::G_FNEARBYINT: + case TargetOpcode::G_FCOPYSIGN: case TargetOpcode::G_FNEG: case TargetOpcode::G_FABS: { Type *HLTy = getFloatTypeForLLT(Ctx, LLTy); diff --git a/llvm/lib/CodeGen/LiveRegUnits.cpp b/llvm/lib/CodeGen/LiveRegUnits.cpp index b2731aa0e7dbca..2406743b27b35a 100644 --- a/llvm/lib/CodeGen/LiveRegUnits.cpp +++ b/llvm/lib/CodeGen/LiveRegUnits.cpp @@ -41,6 +41,24 @@ void LiveRegUnits::addRegsInMask(const uint32_t *RegMask) { } } +void LiveRegUnits::stepForward(const MachineInstr &MI) { + // Remove killed registers and regmask kills from the set. + for (const MachineOperand &MOP : phys_regs_and_masks(MI)) { + if (MOP.isRegMask()) { + removeRegsNotPreserved(MOP.getRegMask()); + continue; + } + + if (MOP.isKill()) + removeReg(MOP.getReg()); + } + + // Add live defs to the set. + for (const MachineOperand &MOP : phys_regs_and_masks(MI)) + if (MOP.isReg() && MOP.isDef() && !MOP.isDead()) + addReg(MOP.getReg()); +} + void LiveRegUnits::stepBackward(const MachineInstr &MI) { // Remove defined registers and regmask kills from the set. for (const MachineOperand &MOP : phys_regs_and_masks(MI)) { diff --git a/llvm/lib/Target/Z80/CMakeLists.txt b/llvm/lib/Target/Z80/CMakeLists.txt index debcbeb3c47a33..34b408a039940a 100644 --- a/llvm/lib/Target/Z80/CMakeLists.txt +++ b/llvm/lib/Target/Z80/CMakeLists.txt @@ -3,6 +3,8 @@ set(LLVM_TARGET_DEFINITIONS Z80.td) tablegen(LLVM EZ80GenAsmWriter.inc -gen-asm-writer -asmwriternum=1) tablegen(LLVM Z80GenAsmWriter.inc -gen-asm-writer) tablegen(LLVM Z80GenCallingConv.inc -gen-callingconv) +tablegen(LLVM Z80GenGICombiner.inc -gen-global-isel-combiner + -combiners="Z80PreLegalizerCombinerHelper") tablegen(LLVM Z80GenGlobalISel.inc -gen-global-isel) tablegen(LLVM Z80GenInstrInfo.inc -gen-instr-info) tablegen(LLVM Z80GenRegisterBank.inc -gen-register-bank) @@ -15,6 +17,7 @@ set(sources GISel/Z80CallLowering.cpp GISel/Z80InstructionSelector.cpp GISel/Z80LegalizerInfo.cpp + GISel/Z80PreLegalizerCombiner.cpp GISel/Z80RegisterBankInfo.cpp Z80AsmPrinter.cpp Z80CallingConv.cpp @@ -22,7 +25,8 @@ set(sources Z80ISelLowering.cpp Z80InstrInfo.cpp Z80MCInstLower.cpp - Z80MCInstLower.cpp + Z80MachineLateOptimization.cpp + Z80PostSelectCombiner.cpp Z80RegisterInfo.cpp Z80Subtarget.cpp Z80TargetMachine.cpp diff --git a/llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp b/llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp index 30fb32b500363a..017c0d123e7217 100644 --- a/llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp +++ b/llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp @@ -36,25 +36,21 @@ struct OutgoingValueHandler : public CallLowering::ValueHandler { MachineInstrBuilder &MIB, CCAssignFn *AssignFn) : ValueHandler(MIRBuilder, MRI, AssignFn), MIB(MIB), DL(MIRBuilder.getMF().getDataLayout()), - STI(MIRBuilder.getMF().getSubtarget()) {} + STI(MIRBuilder.getMF().getSubtarget()) { + LLT PtrTy = LLT::pointer(0, DL.getPointerSizeInBits(0)); + Register SPReg = STI.getRegisterInfo()->getStackRegister(); + SPRegCopy = MIRBuilder.buildCopy(PtrTy, SPReg).getReg(0); + } bool isIncomingArgumentHandler() const override { return false; } - Register getStackAddress(uint64_t Size, int64_t Offset, + Register getStackAddress(uint64_t Size, int64_t Off, MachinePointerInfo &MPO) override { - LLT p0 = LLT::pointer(0, DL.getPointerSizeInBits(0)); - LLT SType = LLT::scalar(DL.getPointerSizeInBits(0)); - Register SPReg = MRI.createGenericVirtualRegister(p0); - MIRBuilder.buildCopy(SPReg, STI.getRegisterInfo()->getStackRegister()); - - Register OffsetReg = MRI.createGenericVirtualRegister(SType); - MIRBuilder.buildConstant(OffsetReg, Offset); - - Register AddrReg = MRI.createGenericVirtualRegister(p0); - MIRBuilder.buildPtrAdd(AddrReg, SPReg, OffsetReg); - - MPO = MachinePointerInfo::getStack(MIRBuilder.getMF(), Offset); - return AddrReg; + LLT PtrTy = LLT::pointer(0, DL.getPointerSizeInBits(0)); + LLT OffTy = LLT::scalar(DL.getIndexSizeInBits(0)); + MPO = MachinePointerInfo::getStack(MIRBuilder.getMF(), Off); + auto OffI = MIRBuilder.buildConstant(OffTy, Off); + return MIRBuilder.buildPtrAdd(PtrTy, SPRegCopy, OffI).getReg(0); } void assignValueToReg(Register ValVReg, Register PhysReg, @@ -83,6 +79,8 @@ struct OutgoingValueHandler : public CallLowering::ValueHandler { VT, CCValAssign::Full); assignValueToReg(FlagsReg, VA.getLocReg(), VA); } + if (MRI.use_empty(SPRegCopy)) + MRI.getVRegDef(SPRegCopy)->eraseFromParent(); return ValueHandler::finalize(State); } @@ -90,6 +88,26 @@ struct OutgoingValueHandler : public CallLowering::ValueHandler { MachineInstrBuilder &MIB; const DataLayout &DL; const Z80Subtarget &STI; + Register SPRegCopy; +}; + +struct TailCallArgHandler : public OutgoingValueHandler { + TailCallArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + MachineInstrBuilder &MIB, CCAssignFn *AssignFn, int FPDiff) + : OutgoingValueHandler(MIRBuilder, MRI, MIB, AssignFn), FPDiff(FPDiff) {} + + Register getStackAddress(uint64_t Size, int64_t Offset, + MachinePointerInfo &MPO) override { + MachineFunction &MF = MIRBuilder.getMF(); + int FI = MF.getFrameInfo().CreateFixedObject(Size, FPDiff + Offset, true); + MPO = MachinePointerInfo::getFixedStack(MF, FI); + return MIRBuilder + .buildFrameIndex(LLT::pointer(0, DL.getPointerSizeInBits(0)), FI) + .getReg(0); + } + +private: + int FPDiff; }; struct CallArgHandler : public OutgoingValueHandler { @@ -113,6 +131,7 @@ struct CallArgHandler : public OutgoingValueHandler { Register getStackAddress(uint64_t Size, int64_t Offset, MachinePointerInfo &MPO) override { + MIRBuilder.setInsertPt(MIRBuilder.getMBB(), std::next(Before)); return OutgoingValueHandler::getStackAddress(Size, Offset, MPO); } @@ -260,10 +279,11 @@ struct FormalArgHandler : public IncomingValueHandler { bool finalize(CCState &State) override { MachineFunction &MF = MIRBuilder.getMF(); - MachineFrameInfo &MFI = MF.getFrameInfo(); + auto &FuncInfo = *MF.getInfo(); + FuncInfo.setArgFrameSize(State.getNextStackOffset()); if (State.isVarArg()) { - Z80MachineFunctionInfo &FuncInfo = *MF.getInfo(); - int FrameIdx = MFI.CreateFixedObject(1, State.getNextStackOffset(), true); + int FrameIdx = MF.getFrameInfo().CreateFixedObject( + 1, State.getNextStackOffset(), true); FuncInfo.setVarArgsFrameIndex(FrameIdx); } return true; @@ -306,6 +326,315 @@ void Z80CallLowering::splitToValueTypes(const ArgInfo &OrigArg, } } +/// Return true if the calling convention is one that we can guarantee TCO for. +static bool canGuaranteeTCO(CallingConv::ID CC) { + return CC == CallingConv::Fast; +} + +/// Return true if we might ever do TCO for calls with this calling convention. +static bool mayTailCallThisCC(CallingConv::ID CC) { + switch (CC) { + case CallingConv::C: + case CallingConv::PreserveMost: + case CallingConv::Z80_LibCall: + case CallingConv::Z80_LibCall_AB: + case CallingConv::Z80_LibCall_AC: + case CallingConv::Z80_LibCall_BC: + case CallingConv::Z80_LibCall_L: + case CallingConv::Z80_LibCall_F: + case CallingConv::Z80_TIFlags: + return true; + default: + return canGuaranteeTCO(CC); + } +} + +bool Z80CallLowering::doCallerAndCalleePassArgsTheSameWay( + CallLoweringInfo &Info, MachineFunction &MF, + SmallVectorImpl &InArgs) const { + const Function &CallerF = MF.getFunction(); + CallingConv::ID CalleeCC = Info.CallConv; + CallingConv::ID CallerCC = CallerF.getCallingConv(); + + // If the calling conventions match, then everything must be the same. + if (CalleeCC == CallerCC) + return true; + + // Check if the caller and callee will handle arguments in the same way. + if (!resultsCompatible(Info, MF, InArgs, CC_Z80, CC_Z80, CC_Z80, CC_Z80)) + return false; + + // Make sure that the caller and callee preserve all of the same registers. + const auto &TRI = *MF.getSubtarget().getRegisterInfo(); + const uint32_t *CallerPreserved = TRI.getCallPreservedMask(MF, CallerCC); + const uint32_t *CalleePreserved = TRI.getCallPreservedMask(MF, CalleeCC); + + return TRI.regmaskSubsetEqual(CallerPreserved, CalleePreserved); +} + +bool Z80CallLowering::areCalleeOutgoingArgsTailCallable( + CallLoweringInfo &Info, MachineFunction &MF, + SmallVectorImpl &OutArgs) const { + // If there are no outgoing arguments, then we are done. + if (OutArgs.empty()) + return true; + + const Function &CallerF = MF.getFunction(); + CallingConv::ID CalleeCC = Info.CallConv; + CallingConv::ID CallerCC = CallerF.getCallingConv(); + + // We have outgoing arguments. Make sure that we can tail call with them. + SmallVector OutLocs; + CCState OutInfo(CalleeCC, false, MF, OutLocs, CallerF.getContext()); + + if (!analyzeArgInfo(OutInfo, OutArgs, CC_Z80, CC_Z80)) { + LLVM_DEBUG(dbgs() << "... Could not analyze call operands.\n"); + return false; + } + + // Make sure that they can fit on the caller's stack. + const auto &FuncInfo = *MF.getInfo(); + if (OutInfo.getNextStackOffset() > FuncInfo.getArgFrameSize()) { + LLVM_DEBUG(dbgs() << "... Cannot fit call operands on caller's stack.\n"); + return false; + } + + // Verify that the parameters in callee-saved registers match. + // TODO: Port this over to CallLowering as general code once swiftself is + // supported. + const auto &TRI = *MF.getSubtarget().getRegisterInfo(); + const uint32_t *CallerPreservedMask = TRI.getCallPreservedMask(MF, CallerCC); + MachineRegisterInfo &MRI = MF.getRegInfo(); + + for (unsigned i = 0; i < OutLocs.size(); ++i) { + auto &ArgLoc = OutLocs[i]; + // If it's not a register, it's fine. + if (!ArgLoc.isRegLoc()) { + if (Info.IsVarArg) { + // Be conservative and disallow variadic memory operands to match SDAG's + // behaviour. + // FIXME: If the caller's calling convention is C, then we can + // potentially use its argument area. However, for cases like fastcc, + // we can't do anything. + LLVM_DEBUG( + dbgs() + << "... Cannot tail call vararg function with stack arguments\n"); + return false; + } + continue; + } + + Register Reg = ArgLoc.getLocReg(); + + // Only look at callee-saved registers. + if (MachineOperand::clobbersPhysReg(CallerPreservedMask, Reg)) + continue; + + LLVM_DEBUG( + dbgs() + << "... Call has an argument passed in a callee-saved register.\n"); + + // Check if it was copied from. + ArgInfo &OutInfo = OutArgs[i]; + + if (OutInfo.Regs.size() > 1) { + LLVM_DEBUG( + dbgs() << "... Cannot handle arguments in multiple registers.\n"); + return false; + } + + // Check if we copy the register, walking through copies from virtual + // registers. Note that getDefIgnoringCopies does not ignore copies from + // physical registers. + MachineInstr *RegDef = getDefIgnoringCopies(OutInfo.Regs[0], MRI); + if (!RegDef || RegDef->getOpcode() != TargetOpcode::COPY) { + LLVM_DEBUG( + dbgs() + << "... Parameter was not copied into a VReg, cannot tail call.\n"); + return false; + } + + // Got a copy. Verify that it's the same as the register we want. + Register CopyRHS = RegDef->getOperand(1).getReg(); + if (CopyRHS != Reg) { + LLVM_DEBUG(dbgs() << "... Callee-saved register was not copied into " + "VReg, cannot tail call.\n"); + return false; + } + } + + return true; +} + +bool Z80CallLowering::isEligibleForTailCallOptimization( + MachineIRBuilder &MIRBuilder, CallLoweringInfo &Info, + SmallVectorImpl &InArgs, SmallVectorImpl &OutArgs) const { + + // Must pass all target-independent checks in order to tail call optimize. + if (!Info.IsTailCall) + return false; + + CallingConv::ID CalleeCC = Info.CallConv; + MachineFunction &MF = MIRBuilder.getMF(); + const Function &CallerF = MF.getFunction(); + + LLVM_DEBUG(dbgs() << "Attempting to lower call as tail call\n"); + + if (Info.SwiftErrorVReg) { + // TODO: We should handle this. + // Note that this is also handled by the check for no outgoing arguments. + // Proactively disabling this though, because the swifterror handling in + // lowerCall inserts a COPY *after* the location of the call. + LLVM_DEBUG(dbgs() << "... Cannot handle tail calls with swifterror yet.\n"); + return false; + } + + if (!mayTailCallThisCC(CalleeCC)) { + LLVM_DEBUG(dbgs() << "... Calling convention cannot be tail called.\n"); + return false; + } + + // Byval parameters hand the function a pointer directly into the stack area + // we want to reuse during a tail call. Working around this *is* possible (see + // X86). + // + // FIXME: In Z80ISelLowering, this isn't worked around. Can/should we try it? + // + // FIXME: Check whether the callee also has an "inreg" argument. + // + // When the caller has a swifterror argument, we don't want to tail call + // because would have to move into the swifterror register before the + // tail call. + if (any_of(CallerF.args(), [](const Argument &A) { + return A.hasByValAttr() || A.hasInRegAttr() || A.hasSwiftErrorAttr(); + })) { + LLVM_DEBUG(dbgs() << "... Cannot tail call from callers with byval, " + "inreg, or swifterror arguments\n"); + return false; + } + + // If we have -tailcallopt, then we're done. + if (MF.getTarget().Options.GuaranteedTailCallOpt) + return canGuaranteeTCO(CalleeCC) && CalleeCC == CallerF.getCallingConv(); + + // We don't have -tailcallopt, so we're allowed to change the ABI (sibcall). + // Try to find cases where we can do that. + + // I want anyone implementing a new calling convention to think long and hard + // about this assert. + assert((!Info.IsVarArg || CalleeCC == CallingConv::C) && + "Unexpected variadic calling convention"); + + // Verify that the incoming and outgoing arguments from the callee are + // safe to tail call. + if (!doCallerAndCalleePassArgsTheSameWay(Info, MF, InArgs)) { + LLVM_DEBUG( + dbgs() + << "... Caller and callee have incompatible calling conventions.\n"); + return false; + } + + if (!areCalleeOutgoingArgsTailCallable(Info, MF, OutArgs)) + return false; + + LLVM_DEBUG(dbgs() << "... Call is eligible for tail call optimization.\n"); + return true; +} + +bool Z80CallLowering::lowerTailCall(MachineIRBuilder &MIRBuilder, + CallLoweringInfo &Info, + SmallVectorImpl &OutArgs) const { + MachineFunction &MF = MIRBuilder.getMF(); + const Function &F = MF.getFunction(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + const auto &STI = MF.getSubtarget(); + const Z80InstrInfo &TII = *STI.getInstrInfo(); + const Z80RegisterInfo &TRI = *STI.getRegisterInfo(); + const auto &FuncInfo = *MF.getInfo(); + + // True when we're tail calling, but without -tailcallopt. + bool IsSibCall = !MF.getTarget().Options.GuaranteedTailCallOpt; + + // TODO: Right now, regbankselect doesn't know how to handle the rtcGPR64 + // register class. Until we can do that, we should fall back here. + if (F.hasFnAttribute("branch-target-enforcement")) { + LLVM_DEBUG( + dbgs() << "Cannot lower indirect tail calls with BTI enabled yet.\n"); + return false; + } + + MachineInstrBuilder CallSeqStart; + if (!IsSibCall) + CallSeqStart = MIRBuilder.buildInstr(TII.getCallFrameSetupOpcode()); + + bool Is24Bit = STI.is24Bit(); + unsigned TCRetOpc = Info.Callee.isReg() + ? Is24Bit ? Z80::TCRETURN24r : Z80::TCRETURN16r + : Is24Bit ? Z80::TCRETURN24 : Z80::TCRETURN16; + auto MIB = MIRBuilder.buildInstrNoInsert(TCRetOpc).add(Info.Callee) + .addRegMask(TRI.getCallPreservedMask(MF, Info.CallConv)); + + // FPDiff is the byte offset of the call's argument area from the callee's. + // Stores to callee stack arguments will be placed in FixedStackSlots offset + // by this amount for a tail call. In a sibling call it must be 0 because the + // caller will deallocate the entire stack and the callee still expects its + // arguments to begin at SP+0. + int FPDiff = 0; + + // This will be 0 for sibcalls, potentially nonzero for tail calls produced + // by -tailcallopt. For sibcalls, the memory operands for the call are + // already available in the caller's incoming argument space. + unsigned NumBytes = 0; + if (!IsSibCall) { + // We aren't sibcalling, so we need to compute FPDiff. We need to do this + // before handling assignments, because FPDiff must be known for memory + // arguments. + unsigned NumReusableBytes = FuncInfo.getArgFrameSize(); + SmallVector OutLocs; + CCState OutInfo(Info.CallConv, false, MF, OutLocs, F.getContext()); + analyzeArgInfo(OutInfo, OutArgs, CC_Z80, CC_Z80); + + // FPDiff will be negative if this tail call requires more space than we + // would automatically have in our incoming argument space. Positive if we + // actually shrink the stack. + FPDiff = NumReusableBytes - NumBytes; + } + + // Do the actual argument marshalling. + SmallVector PhysRegs; + TailCallArgHandler Handler(MIRBuilder, MRI, MIB, CC_Z80, FPDiff); + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, OutArgs, + Handler)) + return false; + + // If we have -tailcallopt, we need to adjust the stack. We'll do the call + // sequence start and end here. + if (!IsSibCall) { + MIB->getOperand(1).setImm(FPDiff); + CallSeqStart.addImm(NumBytes).addImm(0); + // End the call sequence *before* emitting the call. Normally, we would + // tidy the frame up after the call. However, here, we've laid out the + // parameters so that when SP is reset, they will be in the correct + // location. + MIRBuilder.buildInstr(TII.getCallFrameDestroyOpcode()) + .addImm(NumBytes).addImm(0); + } + + // Now we can add the actual call instruction to the correct basic block. + MIRBuilder.insertInstr(MIB); + + // If Callee is a reg, since it is used by a target specific instruction, + // it must have a register class matching the constraint of that instruction. + if (Info.Callee.isReg()) + MIB->getOperand(0).setReg(constrainOperandRegClass( + MF, TRI, MRI, TII, *MF.getSubtarget().getRegBankInfo(), *MIB, + MIB->getDesc(), Info.Callee, 0)); + + MF.getFrameInfo().setHasTailCall(); + Info.LoweredTailCall = true; + return true; +} + bool Z80CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, CallLoweringInfo &Info) const { MachineFunction &MF = MIRBuilder.getMF(); @@ -346,8 +675,38 @@ bool Z80CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, splitToValueTypes(Info.OrigRet, InArgs, DL, MRI); } + bool CanTailCallOpt = + isEligibleForTailCallOptimization(MIRBuilder, Info, InArgs, OutArgs); + + // We must emit a tail call if we have musttail. + if (Info.IsMustTailCall && !CanTailCallOpt) { + // There are types of incoming/outgoing arguments we can't handle yet, so + // it doesn't make sense to actually die here like in ISelLowering. Instead, + // fall back to SelectionDAG and let it try to handle this. + LLVM_DEBUG(dbgs() << "Failed to lower musttail call as tail call\n"); + return false; + } + + if (CanTailCallOpt) + return lowerTailCall(MIRBuilder, Info, OutArgs); + auto CallSeqStart = MIRBuilder.buildInstr(TII.getCallFrameSetupOpcode()); + // Look through bitcasts of the callee. + while (Info.Callee.isReg()) { + if (MachineInstr *MI = MRI.getVRegDef(Info.Callee.getReg())) { + switch (MI->getOpcode()) { + case TargetOpcode::COPY: + case TargetOpcode::G_GLOBAL_VALUE: + case TargetOpcode::G_INTTOPTR: + case TargetOpcode::G_CONSTANT: + Info.Callee = MI->getOperand(1); + continue; + } + } + break; + } + // Create a temporarily-floating call instruction so we can add the implicit // uses of arg registers. bool Is24Bit = STI.is24Bit(); diff --git a/llvm/lib/Target/Z80/GISel/Z80CallLowering.h b/llvm/lib/Target/Z80/GISel/Z80CallLowering.h index b9ff1b59103925..adebd20c3741d6 100644 --- a/llvm/lib/Target/Z80/GISel/Z80CallLowering.h +++ b/llvm/lib/Target/Z80/GISel/Z80CallLowering.h @@ -38,6 +38,24 @@ class Z80CallLowering : public CallLowering { void splitToValueTypes(const ArgInfo &OrigArg, SmallVectorImpl &SplitArgs, const DataLayout &DL, MachineRegisterInfo &MRI) const; + + bool + doCallerAndCalleePassArgsTheSameWay(CallLoweringInfo &Info, + MachineFunction &MF, + SmallVectorImpl &InArgs) const; + + bool + areCalleeOutgoingArgsTailCallable(CallLoweringInfo &Info, MachineFunction &MF, + SmallVectorImpl &OutArgs) const; + + bool + isEligibleForTailCallOptimization(MachineIRBuilder &MIRBuilder, + CallLoweringInfo &Info, + SmallVectorImpl &InArgs, + SmallVectorImpl &OutArgs) const; + + bool lowerTailCall(MachineIRBuilder &MIRBuilder, CallLoweringInfo &Info, + SmallVectorImpl &OutArgs) const; }; } // end namespace llvm diff --git a/llvm/lib/Target/Z80/GISel/Z80InstructionSelector.cpp b/llvm/lib/Target/Z80/GISel/Z80InstructionSelector.cpp index 1e8dd7ab9b9645..64a1a6a38fa34a 100644 --- a/llvm/lib/Target/Z80/GISel/Z80InstructionSelector.cpp +++ b/llvm/lib/Target/Z80/GISel/Z80InstructionSelector.cpp @@ -19,9 +19,12 @@ #include "Z80TargetMachine.h" #include "llvm/CodeGen/GlobalISel/InstructionSelector.h" #include "llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h" +#include "llvm/CodeGen/GlobalISel/MIPatternMatch.h" #include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/Support/Debug.h" + using namespace llvm; +using namespace MIPatternMatch; #define DEBUG_TYPE "Z80-isel" @@ -52,8 +55,8 @@ class Z80InstructionSelector : public InstructionSelector { bool selectSExt(MachineInstr &I, MachineRegisterInfo &MRI) const; bool selectZExt(MachineInstr &I, MachineRegisterInfo &MRI) const; bool selectAnyExt(MachineInstr &I, MachineRegisterInfo &MRI) const; - bool selectLoadStoreOp(MachineInstr &I, MachineRegisterInfo &MRI, - MachineFunction &MF) const; + bool selectLoadStore(MachineInstr &I, MachineRegisterInfo &MRI, + MachineFunction &MF) const; bool selectFrameIndexOrGep(MachineInstr &I, MachineRegisterInfo &MRI, MachineFunction &MF) const; @@ -71,6 +74,8 @@ class Z80InstructionSelector : public InstructionSelector { MachineRegisterInfo &MRI) const; Z80::CondCode foldCond(Register CondReg, MachineIRBuilder &MIB, MachineRegisterInfo &MRI) const; + bool selectShift(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectFunnelShift(MachineInstr &I, MachineRegisterInfo &MRI) const; bool selectSetCond(MachineInstr &I, MachineRegisterInfo &MRI) const; bool selectSelect(MachineInstr &I, MachineRegisterInfo &MRI) const; @@ -288,7 +293,7 @@ bool Z80InstructionSelector::select(MachineInstr &I) const { return selectAnyExt(I, MRI); case TargetOpcode::G_LOAD: case TargetOpcode::G_STORE: - return selectLoadStoreOp(I, MRI, MF); + return selectLoadStore(I, MRI, MF); case TargetOpcode::G_PTR_ADD: case TargetOpcode::G_FRAME_INDEX: return selectFrameIndexOrGep(I, MRI, MF); @@ -298,6 +303,13 @@ bool Z80InstructionSelector::select(MachineInstr &I) const { return selectMergeValues(I, MRI, MF); case TargetOpcode::G_SELECT: return selectSelect(I, MRI); + case TargetOpcode::G_SHL: + case TargetOpcode::G_LSHR: + case TargetOpcode::G_ASHR: + return selectShift(I, MRI); + case TargetOpcode::G_FSHL: + case TargetOpcode::G_FSHR: + return selectFunnelShift(I, MRI); case TargetOpcode::G_UADDO: case TargetOpcode::G_UADDE: case TargetOpcode::G_USUBO: @@ -522,71 +534,246 @@ bool Z80InstructionSelector::selectAnyExt(MachineInstr &I, return true; } -bool Z80InstructionSelector::selectLoadStoreOp(MachineInstr &I, - MachineRegisterInfo &MRI, - MachineFunction &MF) const { - bool IsStore = I.getOpcode() == TargetOpcode::G_STORE; - assert((IsStore || I.getOpcode() == TargetOpcode::G_LOAD) && +static bool canFoldLoad(MachineInstr &LoadMI, MachineInstr &TargetMI) { + assert(LoadMI.mayLoad() && "Expected a load"); + MachineBasicBlock *MBB = LoadMI.getParent(); + if (!MBB || TargetMI.getParent() != MBB) + return false; + for (MachineBasicBlock::iterator I(LoadMI), E(MBB->end()); I != E; ++I) { + if (I->isLoadFoldBarrier()) + return false; + if (I == TargetMI) + return true; + } + return false; +} + +bool Z80InstructionSelector::selectLoadStore(MachineInstr &I, + MachineRegisterInfo &MRI, + MachineFunction &MF) const { + bool IsLoad = I.getOpcode() == TargetOpcode::G_LOAD; + assert((IsLoad || I.getOpcode() == TargetOpcode::G_STORE) && "unexpected instruction"); - Register DefReg = I.getOperand(0).getReg(); + Register ValReg = I.getOperand(0).getReg(); Register PtrReg = I.getOperand(1).getReg(); MachineInstr *PtrMI = MRI.getVRegDef(PtrReg); - LLT Ty = MRI.getType(DefReg); + LLT Ty = MRI.getType(ValReg); + + SmallVector RMWOps; + SmallVector MemMOs; + MemMOs.push_back(&I); + MachineInstr *ValMI = MRI.getVRegDef(ValReg); + if (!IsLoad && Ty == LLT::scalar(8) && ValMI) { + unsigned ValLastOpIdx = ValMI->getNumExplicitOperands() - 1; + if (std::next(MachineBasicBlock::iterator(ValMI)) == I && + ValMI->getNumDefs() == 1 && ValLastOpIdx >= 2) { + Register LoadReg = ValMI->getOperand(1).getReg(); + if (MachineInstr *LoadMI = MRI.getVRegDef(LoadReg)) { + if (LoadMI->getOpcode() == TargetOpcode::G_LOAD && + LoadMI->getOperand(1).getReg() == PtrReg && + canFoldLoad(*LoadMI, *ValMI)) { + assert(LoadMI->hasOneMemOperand() && + "Expected load to have one MMO."); + MemMOs.push_back(LoadMI); + Register ImmReg = ValMI->getOperand(ValLastOpIdx).getReg(); + if (auto OffConst = getConstantVRegVal(ImmReg, MRI)) { + switch (ValMI->getOpcode()) { + case TargetOpcode::G_FSHL: + if (ValMI->getOperand(2).getReg() != LoadReg) + break; + if (*OffConst == 1) + RMWOps = {Z80::RLC8p, Z80::RLC8o}; + else if (*OffConst == 7) + RMWOps = {Z80::RRC8p, Z80::RRC8o}; + break; + case TargetOpcode::G_FSHR: + if (ValMI->getOperand(2).getReg() != LoadReg) + break; + if (*OffConst == 1) + RMWOps = {Z80::RLC8p, Z80::RLC8o}; + else if (*OffConst == 7) + RMWOps = {Z80::RRC8p, Z80::RRC8o}; + break; + case TargetOpcode::G_SHL: + if (*OffConst == 1) + RMWOps = {Z80::SLA8p, Z80::SLA8o}; + break; + case TargetOpcode::G_ASHR: + if (*OffConst == 1) + RMWOps = {Z80::SRA8p, Z80::SRA8o}; + break; + case TargetOpcode::G_LSHR: + if (*OffConst == 1) + RMWOps = {Z80::SRL8p, Z80::SRL8o}; + break; + case TargetOpcode::G_AND: + if (isPowerOf2_32(~*OffConst & 0xFF)) + RMWOps = {Z80::RES8bp, Z80::RES8bo, Log2_32(~*OffConst & 0xFF)}; + break; + case TargetOpcode::G_OR: + if (isPowerOf2_32(*OffConst & 0xFF)) + RMWOps = {Z80::SET8bp, Z80::SET8bo, Log2_32(*OffConst & 0xFF)}; + break; + case TargetOpcode::G_ADD: + if (*OffConst == 1) + RMWOps = {Z80::INC8p, Z80::INC8o}; + else if (*OffConst == -1) + RMWOps = {Z80::DEC8p, Z80::DEC8o}; + break; + } + } + } + } + } + } I.RemoveOperand(1); - if (IsStore) - I.RemoveOperand(0); + I.RemoveOperand(0); MachineInstrBuilder MIB(MF, I); - bool IsOff = false; - int8_t Off = 0; - if (PtrMI) { + SmallVector MOs; + int32_t Off = 0; + while (PtrMI) { switch (PtrMI->getOpcode()) { + case TargetOpcode::G_INTTOPTR: + if (MachineInstr *IntMI = MRI.getVRegDef(PtrMI->getOperand(1).getReg())) + if (IntMI->getOpcode() == TargetOpcode::G_CONSTANT) + MOs.push_back(MachineOperand::CreateImm( + IntMI->getOperand(1).getCImm()->getSExtValue() + Off)); + break; + case TargetOpcode::G_GLOBAL_VALUE: + MOs.push_back(PtrMI->getOperand(1)); + MOs.back().setOffset(MOs.back().getOffset() + Off); + break; case TargetOpcode::G_FRAME_INDEX: - IsOff = true; + MOs.push_back(PtrMI->getOperand(1)); + MOs.push_back(MachineOperand::CreateImm(Off)); break; case TargetOpcode::G_PTR_ADD: if (auto OffConst = getConstantVRegVal(PtrMI->getOperand(2).getReg(), MRI)) { - if (isInt<8>(*OffConst)) { - IsOff = true; - Off = *OffConst; - if (MachineInstr *BaseMI = - MRI.getVRegDef(PtrMI->getOperand(1).getReg())) - if (BaseMI->getOpcode() == TargetOpcode::G_FRAME_INDEX) - PtrMI = BaseMI; + if ((PtrMI = MRI.getVRegDef(PtrMI->getOperand(1).getReg()))) { + Off += *OffConst; + continue; } } break; } + break; } + unsigned Opc; - switch (Ty.getSizeInBits()) { - case 8: - Opc = IsOff ? IsStore ? Z80::LD8og : Z80::LD8go - : IsStore ? Z80::LD8pg : Z80::LD8gp; - break; - case 16: - Opc = STI.has16BitEZ80Ops() ? IsOff ? IsStore ? Z80::LD16or : Z80::LD16ro - : IsStore ? Z80::LD16pr : Z80::LD16rp - : IsOff ? IsStore ? Z80::LD88or : Z80::LD88ro - : IsStore ? Z80::LD88pr : Z80::LD88rp; - break; - case 24: - assert(STI.is24Bit() && "Illegal memory access size."); - Opc = IsOff ? IsStore ? Z80::LD24or : Z80::LD24ro - : IsStore ? Z80::LD24pr : Z80::LD24rp; - break; - default: - return false; + if (MOs.size() == 1 && (MOs[0].isImm() || MOs[0].isGlobal())) { + if (RMWOps.empty()) { + switch (Ty.getSizeInBits()) { + case 8: { + MachineIRBuilder Builder(I); + MachineInstrBuilder CopyI; + if (IsLoad) { + Builder.setInsertPt(Builder.getMBB(), + std::next(Builder.getInsertPt())); + CopyI = Builder.buildCopy(ValReg, Register(Z80::A)); + Opc = Z80::LD8am; + } else { + CopyI = Builder.buildCopy(Register(Z80::A), ValReg); + Opc = Z80::LD8ma; + } + I.setDesc(TII.get(Opc)); + MIB.add(MOs[0]); + MIB.addReg(Z80::A, getDefRegState(IsLoad) | RegState::Implicit); + return RBI.constrainGenericRegister(ValReg, Z80::R8RegClass, MRI) && + constrainSelectedInstRegOperands(I, TII, TRI, RBI); + } + case 16: + if (STI.is24Bit()) + break; + I.setDesc(TII.get(IsLoad ? Z80::LD16rm : Z80::LD16mr)); + if (IsLoad) + MIB.addDef(ValReg); + MIB.add(MOs[0]); + if (!IsLoad) + MIB.addReg(ValReg); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); + case 24: + assert(STI.is24Bit() && "Illegal memory access size."); + I.setDesc(TII.get(IsLoad ? Z80::LD24rm : Z80::LD24mr)); + if (IsLoad) + MIB.addDef(ValReg); + MIB.add(MOs[0]); + if (!IsLoad) + MIB.addReg(ValReg); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); + } + } + MOs.clear(); + Off = 0; + } + + if (MOs.empty()) { + if (Off && isInt<8>(Off)) { + MOs.push_back( + MachineOperand::CreateReg(PtrMI->getOperand(0).getReg(), false)); + MOs.push_back(MachineOperand::CreateImm(Off)); + } else + MOs.push_back(MachineOperand::CreateReg(PtrReg, false)); + } + bool IsOff = MOs.size() == 2; + if (RMWOps.empty()) { + Optional ValConst; + switch (Ty.getSizeInBits()) { + case 8: + if (!IsLoad) + ValConst = getConstantVRegVal(ValReg, MRI); + Opc = IsOff ? IsLoad ? Z80::LD8go : ValConst ? Z80::LD8oi : Z80::LD8og + : IsLoad ? Z80::LD8gp : ValConst ? Z80::LD8pi : Z80::LD8pg; + break; + case 16: + Opc = STI.has16BitEZ80Ops() ? IsOff ? IsLoad ? Z80::LD16ro : Z80::LD16or + : IsLoad ? Z80::LD16rp : Z80::LD16pr + : IsOff ? IsLoad ? Z80::LD88ro : Z80::LD88or + : IsLoad ? Z80::LD88rp : Z80::LD88pr; + break; + case 24: + assert(STI.is24Bit() && "Illegal memory access size."); + Opc = IsOff ? IsLoad ? Z80::LD24ro : Z80::LD24or + : IsLoad ? Z80::LD24rp : Z80::LD24pr; + break; + default: + return false; + } + I.setDesc(TII.get(Opc)); + if (IsLoad) + MIB.addDef(ValReg); + for (auto &MO : MOs) + MIB.add(MO); + if (!IsLoad) { + if (ValConst) + MIB.addImm(*ValConst); + else + MIB.addReg(ValReg); + } + } else { + assert(Ty == LLT::scalar(8) && "Expected RMW operation to be 8 bits"); + I.setDesc(TII.get(RMWOps[IsOff])); + if (RMWOps.size() > 2) + MIB.addImm(RMWOps[2]); + for (auto &MO : MOs) + MIB.add(MO); + I.addImplicitDefUseOperands(MF); + MIB.cloneMergedMemRefs(MemMOs); + if (!MRI.use_empty(ValReg)) { + MachineIRBuilder MIB(I); + MIB.setInsertPt(MIB.getMBB(), ++MIB.getInsertPt()); + auto Reload = + MIB.buildInstr(IsOff ? Z80::LD8go : Z80::LD8gp, {ValReg}, {}); + for (auto &MO : MOs) + Reload.add(MO); + Reload.cloneMemRefs(I); + if (!constrainSelectedInstRegOperands(*Reload, TII, TRI, RBI)) + return false; + ValMI->getOperand(0).setReg(MRI.cloneVirtualRegister(ValReg)); + } } - I.setDesc(TII.get(Opc)); - if (IsOff) - MIB.add(PtrMI->getOperand(1)).addImm(Off); - else - MIB.addReg(PtrReg); - if (IsStore) - MIB.addReg(DefReg); return constrainSelectedInstRegOperands(I, TII, TRI, RBI); } @@ -638,59 +825,37 @@ bool Z80InstructionSelector::selectUnmergeValues(MachineInstr &I, MachineFunction &MF) const { assert((I.getOpcode() == TargetOpcode::G_UNMERGE_VALUES) && "unexpected instruction"); - - switch (MRI.getType(I.getOperand(I.getNumOperands() - 1).getReg()) - .getSizeInBits()) { + MachineIRBuilder MIB(I); + Register LoReg = I.getOperand(0).getReg(); + Register HiReg = I.getOperand(1).getReg(); + Register SrcReg = I.getOperand(I.getNumOperands() - 1).getReg(); + LLT Ty = MRI.getType(SrcReg); + assert(MRI.getType(LoReg) == LLT::scalar(8) && + MRI.getType(HiReg) == LLT::scalar(8) && Ty.isScalar() && + "Illegal type"); + const TargetRegisterClass *RC; + switch (Ty.getSizeInBits()) { case 16: - assert(I.getNumOperands() == 3 && - MRI.getType(I.getOperand(0).getReg()) == LLT::scalar(8) && - MRI.getType(I.getOperand(1).getReg()) == LLT::scalar(8) && - "Illegal instruction"); - BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(TargetOpcode::COPY)) - .add(I.getOperand(0)) - .addReg(I.getOperand(2).getReg(), 0, Z80::sub_low); - BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(TargetOpcode::COPY)) - .add(I.getOperand(1)) - .addReg(I.getOperand(2).getReg(), 0, Z80::sub_high); - if (!RBI.constrainGenericRegister(I.getOperand(0).getReg(), Z80::R8RegClass, - MRI) || - !RBI.constrainGenericRegister(I.getOperand(1).getReg(), Z80::R8RegClass, - MRI) || - !RBI.constrainGenericRegister( - I.getOperand(2).getReg(), - *(STI.is24Bit() ? &Z80::R16RegClass : &Z80::G16RegClass), MRI)) - return false; + assert(I.getNumOperands() == 3 && "Illegal instruction"); + RC = STI.hasIndexHalfRegs() ? &Z80::R16RegClass : &Z80::G16RegClass; + MIB.buildInstr(TargetOpcode::COPY, {LoReg}, {}) + .addReg(SrcReg, 0, Z80::sub_low); + MIB.buildInstr(TargetOpcode::COPY, {HiReg}, {}) + .addReg(SrcReg, 0, Z80::sub_high); break; case 24: { - assert(STI.is24Bit() && "Illegal memory access size."); - assert(I.getNumOperands() == 4 && - MRI.getType(I.getOperand(0).getReg()) == LLT::scalar(8) && - MRI.getType(I.getOperand(1).getReg()) == LLT::scalar(8) && - MRI.getType(I.getOperand(2).getReg()) == LLT::scalar(8) && - "Illegal instruction"); + Register UpReg = I.getOperand(2).getReg(); + assert(STI.is24Bit() && I.getNumOperands() == 4 && + MRI.getType(UpReg) == LLT::scalar(8) && "Illegal instruction"); + RC = &Z80::R24RegClass; int FI = MF.getFrameInfo().CreateStackObject(3, Align(1), false); - BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Z80::LD24or)) - .addFrameIndex(FI) - .addImm(0) - .add(I.getOperand(3)); - BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Z80::LD8ro)) - .add(I.getOperand(2)) - .addFrameIndex(FI) - .addImm(2); - BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(TargetOpcode::COPY)) - .add(I.getOperand(1)) - .addReg(I.getOperand(3).getReg(), 0, Z80::sub_high); - BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(TargetOpcode::COPY)) - .add(I.getOperand(0)) - .addReg(I.getOperand(3).getReg(), 0, Z80::sub_low); - if (!RBI.constrainGenericRegister(I.getOperand(0).getReg(), Z80::R8RegClass, - MRI) || - !RBI.constrainGenericRegister(I.getOperand(1).getReg(), Z80::R8RegClass, - MRI) || - !RBI.constrainGenericRegister(I.getOperand(2).getReg(), Z80::R8RegClass, - MRI) || - !RBI.constrainGenericRegister(I.getOperand(3).getReg(), - Z80::R24RegClass, MRI)) + MIB.buildInstr(Z80::LD24or).addFrameIndex(FI).addImm(0).addReg(SrcReg); + MIB.buildInstr(Z80::LD8ro, {UpReg}, {}).addFrameIndex(FI).addImm(2); + MIB.buildInstr(TargetOpcode::COPY, {HiReg}, {}) + .addReg(SrcReg, 0, Z80::sub_high); + MIB.buildInstr(TargetOpcode::COPY, {LoReg}, {}) + .addReg(SrcReg, 0, Z80::sub_low); + if (!RBI.constrainGenericRegister(UpReg, Z80::R8RegClass, MRI)) return false; break; } @@ -698,7 +863,9 @@ bool Z80InstructionSelector::selectUnmergeValues(MachineInstr &I, llvm_unreachable("Illegal instruction"); } I.eraseFromParent(); - return true; + return RBI.constrainGenericRegister(LoReg, Z80::R8RegClass, MRI) && + RBI.constrainGenericRegister(HiReg, Z80::R8RegClass, MRI) && + RBI.constrainGenericRegister(SrcReg, *RC, MRI); } bool Z80InstructionSelector::selectMergeValues(MachineInstr &I, @@ -706,26 +873,23 @@ bool Z80InstructionSelector::selectMergeValues(MachineInstr &I, MachineFunction &MF) const { assert((I.getOpcode() == TargetOpcode::G_MERGE_VALUES) && "unexpected instruction"); + MachineIRBuilder MIB(I); Register DstReg = I.getOperand(0).getReg(); - MachineInstr *NewI; + const TargetRegisterClass *RC; switch (MRI.getType(DstReg).getSizeInBits()) { - case 16: + case 16: { assert(I.getNumOperands() == 3 && MRI.getType(I.getOperand(1).getReg()) == LLT::scalar(8) && MRI.getType(I.getOperand(2).getReg()) == LLT::scalar(8) && "Illegal instruction"); - - NewI = BuildMI(*I.getParent(), I, I.getDebugLoc(), - TII.get(TargetOpcode::REG_SEQUENCE), DstReg) - .add(I.getOperand(1)) - .addImm(Z80::sub_low) - .add(I.getOperand(2)) - .addImm(Z80::sub_high); - if (!RBI.constrainGenericRegister( - DstReg, *(STI.is24Bit() ? &Z80::R16RegClass : &Z80::G16RegClass), - MRI)) + RC = STI.hasIndexHalfRegs() ? &Z80::R16RegClass : &Z80::G16RegClass; + auto SeqI = MIB.buildInstr(TargetOpcode::REG_SEQUENCE, {DstReg}, + {I.getOperand(1), int64_t(Z80::sub_low), + I.getOperand(2), int64_t(Z80::sub_high)}); + if (!constrainSelectedInstRegOperands(*SeqI, TII, TRI, RBI)) return false; break; + } case 24: { assert(STI.is24Bit() && "Illegal memory access size."); assert(I.getNumOperands() == 4 && @@ -733,40 +897,52 @@ bool Z80InstructionSelector::selectMergeValues(MachineInstr &I, MRI.getType(I.getOperand(2).getReg()) == LLT::scalar(8) && MRI.getType(I.getOperand(3).getReg()) == LLT::scalar(8) && "Illegal instruction"); - int FI = MF.getFrameInfo().CreateStackObject(1, Align(1), false); - auto Store = - BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Z80::LD8or)) - .addFrameIndex(FI) - .addImm(0) - .add(I.getOperand(3)); - if (!constrainSelectedInstRegOperands(*Store, TII, TRI, RBI)) - return false; - Register Temp1Reg = MRI.createVirtualRegister(&Z80::R24RegClass); - Register Temp2Reg = MRI.createVirtualRegister(&Z80::R24RegClass); - BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Z80::LD24ro), Temp1Reg) - .addFrameIndex(FI) - .addImm(-2); - BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Z80::INSERT_SUBREG), - Temp2Reg) - .addReg(Temp1Reg) - .add(I.getOperand(2)) - .addImm(Z80::sub_high); - NewI = BuildMI(*I.getParent(), I, I.getDebugLoc(), - TII.get(Z80::INSERT_SUBREG), DstReg) - .addReg(Temp2Reg) - .add(I.getOperand(1)) - .addImm(Z80::sub_low); - if (!RBI.constrainGenericRegister(DstReg, Z80::R24RegClass, MRI)) - return false; + RC = &Z80::R24RegClass; + Register UpReg = I.getOperand(3).getReg(), TmpReg; + unsigned Amt; + if (mi_match(UpReg, MRI, m_GAShr(m_Reg(TmpReg), m_ICst(Amt))) && + Amt == 7) { + auto AddI = MIB.buildInstr(Z80::RLC8r, {LLT::scalar(8)}, {TmpReg}); + auto SbcI = MIB.buildInstr(Z80::SBC24aa); + SbcI->findRegisterUseOperand(Z80::UHL)->setIsUndef(); + TmpReg = MIB.buildCopy(RC, Register(Z80::UHL)).getReg(0); + if (!constrainSelectedInstRegOperands(*AddI, TII, TRI, RBI) || + !constrainSelectedInstRegOperands(*SbcI, TII, TRI, RBI)) + return false; + } else { + UpReg = {}; + int FI = MF.getFrameInfo().CreateStackObject(1, Align(1), false); + auto Store = MIB.buildInstr(Z80::LD8or).addFrameIndex(FI).addImm(0) + .add(I.getOperand(3)); + if (!constrainSelectedInstRegOperands(*Store, TII, TRI, RBI)) + return false; + TmpReg = MIB.buildInstr(Z80::LD24ro, {RC}, {}) + .addFrameIndex(FI).addImm(-2).getReg(0); + } + if (I.getOperand(2).getReg() != UpReg) { + auto InsertI = + MIB.buildInstr(Z80::INSERT_SUBREG, {RC}, + {TmpReg, I.getOperand(2), int64_t(Z80::sub_high)}); + if (!constrainSelectedInstRegOperands(*InsertI, TII, TRI, RBI)) + return false; + TmpReg = InsertI.getReg(0); + } + if (I.getOperand(1).getReg() != UpReg) { + auto InsertI = + MIB.buildInstr(Z80::INSERT_SUBREG, {RC}, + {TmpReg, I.getOperand(1), int64_t(Z80::sub_low)}); + if (!constrainSelectedInstRegOperands(*InsertI, TII, TRI, RBI)) + return false; + TmpReg = InsertI.getReg(0); + } + MIB.buildCopy(DstReg, TmpReg); break; } default: llvm_unreachable("Illegal instruction"); } - if (!constrainSelectedInstRegOperands(*NewI, TII, TRI, RBI)) - return false; I.eraseFromParent(); - return true; + return RBI.constrainGenericRegister(DstReg, *RC, MRI); } Z80::CondCode @@ -787,8 +963,15 @@ Z80InstructionSelector::foldCompare(MachineInstr &I, MachineIRBuilder &MIB, bool IsSigned, IsSwapped, IsConst; Z80::CondCode CC = Z80::GetBranchConditionForPredicate(Pred, IsSigned, IsSwapped, IsConst); - if (IsSwapped) - std::swap(LHSReg, RHSReg); + auto ConstRHS = getConstantVRegValWithLookThrough(RHSReg, MRI); + if (IsSwapped) { + if (ConstRHS && ConstRHS->Value != int64_t(IsSigned ? maxIntN(OpSize) + : maxUIntN(OpSize))) { + ++ConstRHS->Value; + CC = Z80::GetOppositeBranchCondition(CC); + } else + std::swap(LHSReg, RHSReg); + } unsigned Opc, LDIOpc, AddOpc; Register Reg; @@ -814,22 +997,26 @@ Z80InstructionSelector::foldCompare(MachineInstr &I, MachineIRBuilder &MIB, } if (IsSigned && !OptSize) { - int64_t Off = 1 << (OpSize - 1); + int64_t Sign = minIntN(OpSize); if (OpSize == 8) { - auto CopyRHSToA = MIB.buildCopy(Register(Z80::A), RHSReg); - if (!constrainSelectedInstRegOperands(*CopyRHSToA, TII, TRI, RBI)) - return Z80::COND_INVALID; - auto AddRHS = MIB.buildInstr(Z80::ADD8ai, {}, {Off}); - if (!constrainSelectedInstRegOperands(*AddRHS, TII, TRI, RBI)) - return Z80::COND_INVALID; - auto CopyRHSFromA = MIB.buildCopy(OpTy, Register(Z80::A)); - if (!constrainSelectedInstRegOperands(*CopyRHSFromA, TII, TRI, RBI)) - return Z80::COND_INVALID; - RHSReg = CopyRHSFromA.getReg(0); + if (ConstRHS) + ConstRHS->Value += Sign; + else { + auto CopyRHSToA = MIB.buildCopy(Register(Z80::A), RHSReg); + if (!constrainSelectedInstRegOperands(*CopyRHSToA, TII, TRI, RBI)) + return Z80::COND_INVALID; + auto AddRHS = MIB.buildInstr(Z80::ADD8ai, {}, {Sign}); + if (!constrainSelectedInstRegOperands(*AddRHS, TII, TRI, RBI)) + return Z80::COND_INVALID; + auto CopyRHSFromA = MIB.buildCopy(OpTy, Register(Z80::A)); + if (!constrainSelectedInstRegOperands(*CopyRHSFromA, TII, TRI, RBI)) + return Z80::COND_INVALID; + RHSReg = CopyRHSFromA.getReg(0); + } auto CopyLHSToA = MIB.buildCopy(Register(Z80::A), LHSReg); if (!constrainSelectedInstRegOperands(*CopyLHSToA, TII, TRI, RBI)) return Z80::COND_INVALID; - auto AddLHS = MIB.buildInstr(Z80::ADD8ai, {}, {Off}); + auto AddLHS = MIB.buildInstr(Z80::ADD8ai, {}, {Sign}); if (!constrainSelectedInstRegOperands(*AddLHS, TII, TRI, RBI)) return Z80::COND_INVALID; auto CopyLHSFromA = MIB.buildCopy(OpTy, Register(Z80::A)); @@ -839,17 +1026,21 @@ Z80InstructionSelector::foldCompare(MachineInstr &I, MachineIRBuilder &MIB, if (!RBI.constrainGenericRegister(LHSReg, Z80::R8RegClass, MRI)) return Z80::COND_INVALID; } else { - auto LDI = MIB.buildInstr(LDIOpc, {OpTy}, {Off}); + auto LDI = MIB.buildInstr(LDIOpc, {OpTy}, {Sign}); if (!constrainSelectedInstRegOperands(*LDI, TII, TRI, RBI)) return Z80::COND_INVALID; + if (ConstRHS) + ConstRHS->Value += Sign; + else { + auto AddRHS = MIB.buildInstr(AddOpc, {OpTy}, {RHSReg, LDI}); + if (!constrainSelectedInstRegOperands(*AddRHS, TII, TRI, RBI)) + return Z80::COND_INVALID; + RHSReg = AddRHS.getReg(0); + } auto AddLHS = MIB.buildInstr(AddOpc, {OpTy}, {LHSReg, LDI}); if (!constrainSelectedInstRegOperands(*AddLHS, TII, TRI, RBI)) return Z80::COND_INVALID; LHSReg = AddLHS.getReg(0); - auto AddRHS = MIB.buildInstr(AddOpc, {OpTy}, {RHSReg, LDI}); - if (!constrainSelectedInstRegOperands(*AddRHS, TII, TRI, RBI)) - return Z80::COND_INVALID; - RHSReg = AddRHS.getReg(0); } switch (CC) { default: @@ -863,10 +1054,43 @@ Z80InstructionSelector::foldCompare(MachineInstr &I, MachineIRBuilder &MIB, } } - auto Copy = MIB.buildCopy(Reg, LHSReg); - if (!constrainSelectedInstRegOperands(*Copy, TII, TRI, RBI)) - return Z80::COND_INVALID; - auto Cmp = MIB.buildInstr(Opc, {}, {RHSReg}); + SmallVector Ops = {RHSReg}; + if (ConstRHS) { + if (!ConstRHS->Value && (CC == Z80::COND_Z || CC == Z80::COND_NZ)) { + if (OpSize == 8) { + Register SrcReg; + uint8_t Mask; + if (mi_match(LHSReg, MRI, + m_OneUse(m_GAnd(m_Reg(SrcReg), m_ICst(Mask)))) && + isPowerOf2_32(Mask)) { + Opc = Z80::BIT8bg; + Reg = {}; + Ops = {uint64_t(findFirstSet(Mask)), SrcReg}; + } else { + Opc = Z80::OR8ar; + Ops = {Reg}; + } + } else { + Opc = OpSize == 24 ? Z80::CP24a0 : Z80::CP16a0; + Ops.clear(); + } + } else if (OpSize == 8) { + Opc = Z80::CP8ai; + Ops = {ConstRHS->Value}; + } else { + auto LDI = MIB.buildInstr(LDIOpc, {OpTy}, {ConstRHS->Value}); + if (!constrainSelectedInstRegOperands(*LDI, TII, TRI, RBI)) + return Z80::COND_INVALID; + Ops = {LDI.getReg(0)}; + } + } + + if (Reg.isValid()) { + auto Copy = MIB.buildCopy(Reg, LHSReg); + if (!constrainSelectedInstRegOperands(*Copy, TII, TRI, RBI)) + return Z80::COND_INVALID; + } + auto Cmp = MIB.buildInstr(Opc, {}, Ops); if (!constrainSelectedInstRegOperands(*Cmp, TII, TRI, RBI)) return Z80::COND_INVALID; if (IsSigned && OptSize) { @@ -981,8 +1205,8 @@ Z80::CondCode Z80InstructionSelector::foldCond(Register CondReg, MachineInstr *CondDef = nullptr; while (MachineInstr *LookthroughDef = MRI.getVRegDef(CondReg)) { - //if (LookthroughDef != MIB.getInsertPt() && !MRI.hasOneUse(CondReg)) - // break; + if (LookthroughDef != MIB.getInsertPt() && !MRI.hasOneUse(CondReg)) + break; CondDef = LookthroughDef; unsigned Opc = CondDef->getOpcode(); if (Opc != TargetOpcode::COPY && Opc != TargetOpcode::G_TRUNC) @@ -994,7 +1218,7 @@ Z80::CondCode Z80InstructionSelector::foldCond(Register CondReg, } Z80::CondCode CC = Z80::COND_INVALID; - if (CondDef && MIB.getInsertPt() == *CondDef) { + if (CondDef) { switch (CondDef->getOpcode()) { case TargetOpcode::G_ICMP: CC = foldCompare(*CondDef, MIB, MRI); @@ -1027,6 +1251,85 @@ Z80::CondCode Z80InstructionSelector::foldCond(Register CondReg, return CC; } +bool Z80InstructionSelector::selectShift(MachineInstr &I, + MachineRegisterInfo &MRI) const { + assert(I.getOpcode() == Z80::G_ASHR && "Unexpected opcode"); + Register DstReg = I.getOperand(0).getReg(); + Register SrcReg = I.getOperand(1).getReg(); + auto Amt = getConstantVRegValWithLookThrough(I.getOperand(2).getReg(), MRI); + assert(Amt && "Expected constant shift amount"); + LLT Ty = MRI.getType(DstReg); + assert(Ty.isScalar() && "Illegal type"); + if (Amt->Value == Ty.getSizeInBits() - 1) { + MachineIRBuilder MIB(I); + Register Reg; + unsigned AddOpc, SbcOpc; + const TargetRegisterClass *RC; + switch (Ty.getSizeInBits()) { + default: + llvm_unreachable("Illegal type"); + case 8: + Reg = Z80::A; + AddOpc = Z80::RLC8r; + SbcOpc = Z80::SBC8ar; + RC = &Z80::R8RegClass; + break; + case 16: + Reg = Z80::HL; + AddOpc = Z80::ADD16aa; + SbcOpc = Z80::SBC16aa; + RC = &Z80::R16RegClass; + break; + case 24: + assert(STI.is24Bit() && "Illegal type"); + Reg = Z80::UHL; + AddOpc = Z80::ADD24aa; + SbcOpc = Z80::SBC24aa; + RC = &Z80::R24RegClass; + break; + } + if (Ty != LLT::scalar(8)) { + MIB.buildCopy(Reg, SrcReg); + if (!RBI.constrainGenericRegister(SrcReg, *RC, MRI)) + return false; + } + auto AddI = MIB.buildInstr(AddOpc, {Ty}, {SrcReg}); + auto SbcI = MIB.buildInstr(SbcOpc); + if (Ty == LLT::scalar(8)) { + SbcI->findRegisterUseOperand(Reg)->setIsUndef(); + SbcI.addReg(Reg, RegState::Undef); + } + MIB.buildCopy(DstReg, Reg); + I.eraseFromParent(); + return constrainSelectedInstRegOperands(*AddI, TII, TRI, RBI) && + constrainSelectedInstRegOperands(*SbcI, TII, TRI, RBI) && + RBI.constrainGenericRegister(DstReg, *RC, MRI); + } + llvm_unreachable("Illegal shift amount"); +} + +bool Z80InstructionSelector::selectFunnelShift(MachineInstr &I, + MachineRegisterInfo &MRI) const { + bool IsLeft = I.getOpcode() == Z80::G_FSHL; + assert((IsLeft || I.getOpcode() == Z80::G_FSHR) && "Unexpected opcode"); + Register DstReg = I.getOperand(0).getReg(); + Register HiReg = I.getOperand(1).getReg(); + Register LoReg = I.getOperand(2).getReg(); + auto Amt = getConstantVRegValWithLookThrough(I.getOperand(3).getReg(), MRI); + LLT Ty = MRI.getType(DstReg); + assert(Ty == LLT::scalar(8) && "Illegal type"); + assert(Amt && (Amt->Value == 1 || Amt->Value == 7) && "Illegal shift amount"); + IsLeft ^= Amt->Value == 7; + MachineIRBuilder MIB(I); + auto ShiftI = MIB.buildInstr(IsLeft ? Z80::RLC8r : Z80::RRC8r, {Ty}, + {IsLeft ? LoReg : HiReg}); + auto RotateI = MIB.buildInstr(IsLeft ? Z80::RL8r : Z80::RR8r, {DstReg}, + {IsLeft ? HiReg : LoReg}); + I.eraseFromParent(); + return constrainSelectedInstRegOperands(*ShiftI, TII, TRI, RBI) && + constrainSelectedInstRegOperands(*RotateI, TII, TRI, RBI); +} + bool Z80InstructionSelector::selectSetCond(MachineInstr &I, MachineRegisterInfo &MRI) const { Register CondReg = I.getOperand(I.getNumExplicitDefs() - 1).getReg(); diff --git a/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.cpp b/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.cpp index a72f9e522664f2..eb21a5c8bf1ef0 100644 --- a/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.cpp +++ b/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.cpp @@ -121,7 +121,7 @@ Z80LegalizerInfo::Z80LegalizerInfo(const Z80Subtarget &STI, .clampScalar(0, s8, s32); getActionDefinitionsBuilder({G_SHL, G_LSHR, G_ASHR}) - .libcallForCartesianProduct(LegalLibcallScalars, {s8}) + .customForCartesianProduct(LegalLibcallScalars, {s8}) .clampScalar(1, s8, s8) .clampScalar(0, s8, s64); @@ -134,6 +134,12 @@ Z80LegalizerInfo::Z80LegalizerInfo(const Z80Subtarget &STI, G_FRINT, G_FNEARBYINT}) .libcallFor({s32, s64}); + getActionDefinitionsBuilder(G_FCOPYSIGN) + .libcallFor({{s32, s32}, {s64, s64}}); + + //getActionDefinitionsBuilder({G_FNEG, G_FABS) + // .customFor({s32, s64}); + getActionDefinitionsBuilder(G_FPTRUNC) .libcallFor({{s32, s64}}); @@ -215,6 +221,10 @@ Z80LegalizerInfo::legalizeCustomMaybeLegal(LegalizerHelper &Helper, return legalizeFConstant(Helper, MI); case TargetOpcode::G_VASTART: return legalizeVAStart(Helper, MI); + case TargetOpcode::G_SHL: + case TargetOpcode::G_LSHR: + case TargetOpcode::G_ASHR: + return legalizeShift(Helper, MI); case TargetOpcode::G_FSHL: case TargetOpcode::G_FSHR: return legalizeFunnelShift(Helper, MI); @@ -270,6 +280,36 @@ Z80LegalizerInfo::legalizeVAStart(LegalizerHelper &Helper, return LegalizerHelper::Legalized; } +LegalizerHelper::LegalizeResult +Z80LegalizerInfo::legalizeShift(LegalizerHelper &Helper, + MachineInstr &MI) const { + assert((MI.getOpcode() == TargetOpcode::G_SHL || + MI.getOpcode() == TargetOpcode::G_LSHR || + MI.getOpcode() == TargetOpcode::G_ASHR) && + "Unexpected opcode"); + MachineRegisterInfo &MRI = *Helper.MIRBuilder.getMRI(); + Register DstReg = MI.getOperand(0).getReg(); + LLT Ty = MRI.getType(DstReg); + if (auto Amt = + getConstantVRegValWithLookThrough(MI.getOperand(2).getReg(), MRI)) { + if (Ty == LLT::scalar(8) && Amt->Value == 1) + return LegalizerHelper::AlreadyLegal; + if (MI.getOpcode() == TargetOpcode::G_SHL && Amt->Value == 1) { + Helper.Observer.changingInstr(MI); + MI.setDesc(Helper.MIRBuilder.getTII().get(TargetOpcode::G_ADD)); + MI.getOperand(2).setReg(MI.getOperand(1).getReg()); + Helper.Observer.changedInstr(MI); + return LegalizerHelper::Legalized; + } + if (MI.getOpcode() == TargetOpcode::G_ASHR && + Amt->Value == Ty.getSizeInBits() - 1 && + (Ty == LLT::scalar(8) || Ty == LLT::scalar(16) || + (Subtarget.is24Bit() && Ty == LLT::scalar(24)))) + return LegalizerHelper::Legalized; + } + return Helper.libcall(MI); +} + LegalizerHelper::LegalizeResult Z80LegalizerInfo::legalizeFunnelShift(LegalizerHelper &Helper, MachineInstr &MI) const { @@ -283,6 +323,9 @@ Z80LegalizerInfo::legalizeFunnelShift(LegalizerHelper &Helper, Register RevReg = MI.getOperand(2).getReg(); Register AmtReg = MI.getOperand(3).getReg(); LLT Ty = MRI.getType(DstReg); + if (auto Amt = getConstantVRegValWithLookThrough(AmtReg, MRI)) + if (Ty == LLT::scalar(8) && (Amt->Value == 1 || Amt->Value == 7)) + return LegalizerHelper::AlreadyLegal; unsigned FwdShiftOpc = TargetOpcode::G_SHL; unsigned RevShiftOpc = TargetOpcode::G_LSHR; diff --git a/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.h b/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.h index a883eff794c1a9..865ba078721880 100644 --- a/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.h +++ b/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.h @@ -45,6 +45,8 @@ class Z80LegalizerInfo : public LegalizerInfo { MachineInstr &MI) const; LegalizerHelper::LegalizeResult legalizeVAStart(LegalizerHelper &Helper, MachineInstr &MI) const; + LegalizerHelper::LegalizeResult legalizeShift(LegalizerHelper &Helper, + MachineInstr &MI) const; LegalizerHelper::LegalizeResult legalizeFunnelShift(LegalizerHelper &Helper, MachineInstr &MI) const; LegalizerHelper::LegalizeResult legalizeCompare(LegalizerHelper &Helper, diff --git a/llvm/lib/Target/Z80/GISel/Z80PreLegalizerCombiner.cpp b/llvm/lib/Target/Z80/GISel/Z80PreLegalizerCombiner.cpp new file mode 100644 index 00000000000000..035283ff67cc32 --- /dev/null +++ b/llvm/lib/Target/Z80/GISel/Z80PreLegalizerCombiner.cpp @@ -0,0 +1,139 @@ +//=== lib/CodeGen/GlobalISel/Z80PreLegalizerCombiner.cpp ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This pass does combining of machine instructions at the generic MI level, +// before the legalizer. +// +//===----------------------------------------------------------------------===// + +#include "Z80.h" +#include "llvm/CodeGen/GlobalISel/Combiner.h" +#include "llvm/CodeGen/GlobalISel/CombinerHelper.h" +#include "llvm/CodeGen/GlobalISel/CombinerInfo.h" +#include "llvm/CodeGen/GlobalISel/GISelKnownBits.h" +#include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/Support/Debug.h" +#include "llvm/Target/TargetMachine.h" + +#define DEBUG_TYPE "z80-prelegalizer-combiner" + +using namespace llvm; + +#define Z80PRELEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_DEPS +#include "Z80GenGICombiner.inc" +#undef Z80PRELEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_DEPS + +namespace { +#define Z80PRELEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_H +#include "Z80GenGICombiner.inc" +#undef Z80PRELEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_H + +class Z80PreLegalizerCombinerInfo : public CombinerInfo { + GISelKnownBits *KB; + MachineDominatorTree *MDT; + Z80GenPreLegalizerCombinerHelperRuleConfig GeneratedRuleCfg; + +public: + Z80PreLegalizerCombinerInfo(bool EnableOpt, bool OptSize, bool MinSize, + GISelKnownBits *KB, MachineDominatorTree *MDT) + : CombinerInfo(/*AllowIllegalOps*/ true, /*ShouldLegalizeIllegal*/ false, + /*LegalizerInfo*/ nullptr, EnableOpt, OptSize, MinSize), + KB(KB), MDT(MDT) { + if (!GeneratedRuleCfg.parseCommandLineOption()) + report_fatal_error("Invalid rule identifier"); + } + + virtual bool combine(GISelChangeObserver &Observer, MachineInstr &MI, + MachineIRBuilder &B) const override; +}; + +bool Z80PreLegalizerCombinerInfo::combine(GISelChangeObserver &Observer, + MachineInstr &MI, + MachineIRBuilder &B) const { + CombinerHelper Helper(Observer, B, KB, MDT); + Z80GenPreLegalizerCombinerHelper Generated(GeneratedRuleCfg, Helper); + return Generated.tryCombineAll(Observer, MI, B, Helper); +} + +#define Z80PRELEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_CPP +#include "Z80GenGICombiner.inc" +#undef Z80PRELEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_CPP + +// Pass boilerplate +// ================ + +class Z80PreLegalizerCombiner : public MachineFunctionPass { +public: + static char ID; + + Z80PreLegalizerCombiner(bool IsOptNone = false); + + StringRef getPassName() const override { + return "Z80 Pre-Legalizer Combiner"; + } + + bool runOnMachineFunction(MachineFunction &MF) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override; +private: + bool IsOptNone; +}; +} // end anonymous namespace + +void Z80PreLegalizerCombiner::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); + AU.setPreservesCFG(); + getSelectionDAGFallbackAnalysisUsage(AU); + AU.addRequired(); + AU.addPreserved(); + if (!IsOptNone) { + AU.addRequired(); + AU.addPreserved(); + } + MachineFunctionPass::getAnalysisUsage(AU); +} + +Z80PreLegalizerCombiner::Z80PreLegalizerCombiner(bool IsOptNone) + : MachineFunctionPass(ID), IsOptNone(IsOptNone) { + initializeZ80PreLegalizerCombinerPass(*PassRegistry::getPassRegistry()); +} + +bool Z80PreLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) { + if (MF.getProperties().hasProperty( + MachineFunctionProperties::Property::FailedISel)) + return false; + auto *TPC = &getAnalysis(); + const Function &F = MF.getFunction(); + bool EnableOpt = + MF.getTarget().getOptLevel() != CodeGenOpt::None && !skipFunction(F); + GISelKnownBits *KB = &getAnalysis().get(MF); + MachineDominatorTree *MDT = + IsOptNone ? nullptr : &getAnalysis(); + Z80PreLegalizerCombinerInfo PCInfo(EnableOpt, F.hasOptSize(), F.hasMinSize(), + KB, MDT); + Combiner C(PCInfo, TPC); + return C.combineMachineInstrs(MF, /*CSEInfo*/ nullptr); +} + +char Z80PreLegalizerCombiner::ID = 0; +INITIALIZE_PASS_BEGIN(Z80PreLegalizerCombiner, DEBUG_TYPE, + "Combine Z80 machine instrs before legalization", + false, false) +INITIALIZE_PASS_DEPENDENCY(TargetPassConfig) +INITIALIZE_PASS_DEPENDENCY(GISelKnownBitsAnalysis) +INITIALIZE_PASS_END(Z80PreLegalizerCombiner, DEBUG_TYPE, + "Combine Z80 machine instrs before legalization", false, + false) + + +namespace llvm { +FunctionPass *createZ80PreLegalizeCombiner(bool IsOptNone) { + return new Z80PreLegalizerCombiner(IsOptNone); +} +} // end namespace llvm diff --git a/llvm/lib/Target/Z80/Z80.h b/llvm/lib/Target/Z80/Z80.h index 9d1cd16c41fb16..de34b070071981 100644 --- a/llvm/lib/Target/Z80/Z80.h +++ b/llvm/lib/Target/Z80/Z80.h @@ -24,12 +24,16 @@ class Z80RegisterBankInfo; class Z80Subtarget; class Z80TargetMachine; +FunctionPass *createZ80PreLegalizeCombiner(bool IsOptNone); InstructionSelector *createZ80InstructionSelector(const Z80TargetMachine &TM, Z80Subtarget &, Z80RegisterBankInfo &); +FunctionPass *createZ80PostSelectCombiner(); +FunctionPass *createZ80MachineLateOptimizationPass(); void initializeZ80PreLegalizerCombinerPass(PassRegistry &); void initializeZ80PostSelectCombinerPass(PassRegistry &); +void initializeZ80MachineLateOptimizationPass(PassRegistry &); } // namespace llvm diff --git a/llvm/lib/Target/Z80/Z80.td b/llvm/lib/Target/Z80/Z80.td index f35763c41761c8..8e603e997c8169 100644 --- a/llvm/lib/Target/Z80/Z80.td +++ b/llvm/lib/Target/Z80/Z80.td @@ -62,6 +62,7 @@ include "Z80RegisterBanks.td" //===----------------------------------------------------------------------===// include "Z80InstrInfo.td" +include "Z80Combine.td" def Z80InstrInfo : InstrInfo; diff --git a/llvm/lib/Target/Z80/Z80Combine.td b/llvm/lib/Target/Z80/Z80Combine.td new file mode 100644 index 00000000000000..d6a3e426cb6211 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80Combine.td @@ -0,0 +1,22 @@ +//=- Z80.td - Define Z80 Combine Rules -----------------------*- tablegen -*-=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +include "llvm/Target/GlobalISel/Combine.td" + +def Z80PreLegalizerCombinerHelper + : GICombinerHelper<"Z80GenPreLegalizerCombinerHelper", [ + trivial_combines, elide_br_by_inverting_cond, ptr_add_immed_chain, + ptr_add_global_immed, ptr_add_const_immed, sext_to_zext, + or_to_add, funnel_shift, combine_identity, split_conditions, + flip_condition, lower_is_power_of_two, sink_const, undef_combines + ]> { + let DisableRuleOption = "z80prelegalizercombiner-disable-rule"; +} diff --git a/llvm/lib/Target/Z80/Z80FrameLowering.cpp b/llvm/lib/Target/Z80/Z80FrameLowering.cpp index 7aae34bb3fb347..cf608b7ea22393 100644 --- a/llvm/lib/Target/Z80/Z80FrameLowering.cpp +++ b/llvm/lib/Target/Z80/Z80FrameLowering.cpp @@ -39,6 +39,10 @@ bool Z80FrameLowering::hasFP(const MachineFunction &MF) const { return MF.getTarget().Options.DisableFramePointerElim(MF) || MF.getFrameInfo().hasStackObjects(); } +bool Z80FrameLowering::isFPSaved(const MachineFunction &MF) const { + return hasFP(MF) && MF.getInfo()->getUsesAltFP() == + Z80MachineFunctionInfo::AFPM_None; +} bool Z80FrameLowering::hasReservedCallFrame(const MachineFunction &MF) const { return false; // call frames are not implemented yet } @@ -75,26 +79,28 @@ Z80FrameLowering::getOptimalStackAdjustmentMethod(MachineFunction &MF, StackAdjustmentMethod BestMethod = PopPushCount ? SAM_Small : SAM_Tiny; unsigned BestCost = PopPushCount * PopPushCost + IncDecCount * IncDecCost; - if (UnknownOffset || (FPOffset >= 0 && FPOffset == Offset)) { - // Optimal if we are trying to set SP = FP, except for tiny Offset - unsigned AllCost = 0; - // LD SP, FP - AllCost += OptSize || HasEZ80Ops ? 2 : 10; + if (isFPSaved(MF)) { + if (UnknownOffset || (FPOffset >= 0 && FPOffset == Offset)) { + // Optimal if we are trying to set SP = FP, except for tiny Offset + unsigned AllCost = 0; + // LD SP, FP + AllCost += OptSize || HasEZ80Ops ? 2 : 10; - return AllCost <= BestCost ? SAM_All : BestMethod; - } + return AllCost <= BestCost ? SAM_All : BestMethod; + } - if (HasEZ80Ops && FPOffset >= 0 && isInt<8>(Offset - FPOffset) && hasFP(MF)) { - // Optimal for medium offsets - unsigned MediumCost = 0; - // LEA , FP - Offset - FPOffset - MediumCost += 3; - // LD SP, - MediumCost += ScratchIsIndex ? 2 : 1; - - if (MediumCost < BestCost) { - BestMethod = SAM_Medium; - BestCost = MediumCost; + if (HasEZ80Ops && FPOffset >= 0 && isInt<8>(Offset - FPOffset)) { + // Optimal for medium offsets + unsigned MediumCost = 0; + // LEA , FP - Offset - FPOffset + MediumCost += 3; + // LD SP, + MediumCost += ScratchIsIndex ? 2 : 1; + + if (MediumCost < BestCost) { + BestMethod = SAM_Medium; + BestCost = MediumCost; + } } } @@ -199,8 +205,9 @@ void Z80FrameLowering::emitPrologue(MachineFunction &MF, return; } Register FrameReg = TRI->getFrameRegister(MF); - BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) - .addUse(FrameReg); + if (isFPSaved(MF)) + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addUse(FrameReg); BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::LD24ri : Z80::LD16ri), FrameReg) .addImm(0); BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::ADD24SP : Z80::ADD16SP), @@ -269,7 +276,7 @@ void Z80FrameLowering::emitEpilogue(MachineFunction &MF, BuildStackAdjustment(MF, MBB, MI, DL, *ScratchReg, StackSize, HasFP ? StackSize : -1, MFI.hasVarSizedObjects()); - if (HasFP) + if (isFPSaved(MF)) BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::POP24r : Z80::POP16r), TRI->getFrameRegister(MF)); } @@ -302,11 +309,38 @@ void Z80FrameLowering::shadowCalleeSavedRegisters( .setMIFlag(Flag); } +static Z80MachineFunctionInfo::AltFPMode +shouldUseAltFP(MachineFunction &MF, MCRegister AltFPReg, + const TargetRegisterInfo *TRI) { + if (MF.getFunction().hasOptSize() || + MF.getFrameInfo().hasVarSizedObjects() || + MF.getTarget().Options.DisableFramePointerElim(MF)) + return Z80MachineFunctionInfo::AFPM_None; + if (!MF.getRegInfo().isPhysRegUsed(Z80::UIY)) + return Z80MachineFunctionInfo::AFPM_Full; + MachineBasicBlock::iterator LastFrameIdx; + bool AltFPModified = false; + for (const MachineBasicBlock &MBB : MF) { + for (const MachineInstr &MI : MBB) { + if (AltFPModified) { + for (const MachineOperand &MO : MI.operands()) + if (MO.isFI() || (MO.isRegMask() && MO.clobbersPhysReg(AltFPReg))) + return Z80MachineFunctionInfo::AFPM_None; + } else if (MI.modifiesRegister(AltFPReg, TRI)) + AltFPModified = true; + } + AltFPModified = true; + } + return Z80MachineFunctionInfo::AFPM_Partial; +} + bool Z80FrameLowering::assignCalleeSavedSpillSlots( MachineFunction &MF, const TargetRegisterInfo *TRI, std::vector &CSI) const { - MF.getInfo()->setCalleeSavedFrameSize( - (CSI.size() + hasFP(MF)) * SlotSize); + auto &FuncInfo = *MF.getInfo(); + FuncInfo.setUsesAltFP(shouldUseAltFP(MF, Is24Bit ? Z80::UIY : Z80::IY, TRI)); + MF.getRegInfo().freezeReservedRegs(MF); + FuncInfo.setCalleeSavedFrameSize((CSI.size() + isFPSaved(MF)) * SlotSize); return true; } @@ -397,7 +431,7 @@ void Z80FrameLowering::processFunctionBeforeFrameFinalized( for (int I = MFI.getObjectIndexBegin(); I < 0; ++I) MinFixedObjOffset = std::min(MinFixedObjOffset, MFI.getObjectOffset(I)); int FI = MFI.CreateFixedSpillStackObject( - SlotSize, MinFixedObjOffset - SlotSize * (1 + hasFP(MF))); + SlotSize, MinFixedObjOffset - SlotSize * (1 + isFPSaved(MF))); RS->addScavengingFrameIndex(FI); } } diff --git a/llvm/lib/Target/Z80/Z80FrameLowering.h b/llvm/lib/Target/Z80/Z80FrameLowering.h index 8b61238a8fe8c9..da3d98d623f053 100644 --- a/llvm/lib/Target/Z80/Z80FrameLowering.h +++ b/llvm/lib/Target/Z80/Z80FrameLowering.h @@ -62,6 +62,7 @@ class Z80FrameLowering : public TargetFrameLowering { MachineBasicBlock::iterator MI) const override; bool hasFP(const MachineFunction &MF) const override; + bool isFPSaved(const MachineFunction &MF) const; bool hasReservedCallFrame(const MachineFunction &MF) const override; bool needsFrameIndexResolution(const MachineFunction &MF) const override; diff --git a/llvm/lib/Target/Z80/Z80InstrInfo.cpp b/llvm/lib/Target/Z80/Z80InstrInfo.cpp index 995251aac4833f..ba8a248117ba8d 100644 --- a/llvm/lib/Target/Z80/Z80InstrInfo.cpp +++ b/llvm/lib/Target/Z80/Z80InstrInfo.cpp @@ -15,6 +15,7 @@ #include "MCTargetDesc/Z80MCTargetDesc.h" #include "Z80.h" #include "Z80Subtarget.h" +#include "llvm/ADT/Sequence.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineRegisterInfo.h" @@ -1430,42 +1431,148 @@ bool Z80InstrInfo::optimizeCompareInstr(MachineInstr &CmpInstr, Register SrcReg, } } +MachineInstr *Z80InstrInfo::optimizeLoadInstr(MachineInstr &MI, + const MachineRegisterInfo *MRI, + unsigned &FoldAsLoadDefReg, + MachineInstr *&DefMI) const { + // Check whether we can move DefMI here. + DefMI = MRI->getVRegDef(FoldAsLoadDefReg); + bool SawStore = false; + if (!DefMI || !DefMI->isSafeToMove(nullptr, SawStore)) + return nullptr; + + // Collect information about virtual register operands of MI. + SmallVector SrcOperandIds; + for (unsigned i = 0, e = MI.getNumOperands(); i != e; ++i) { + MachineOperand &MO = MI.getOperand(i); + if (!MO.isReg()) + continue; + Register Reg = MO.getReg(); + if (Reg != FoldAsLoadDefReg) + continue; + // Do not fold if we have a subreg use or a def. + if (MO.getSubReg() || MO.isDef()) + return nullptr; + SrcOperandIds.push_back(i); + } + if (SrcOperandIds.empty()) + return nullptr; + + // Check whether we can fold the def into SrcOperandId. + if (MachineInstr *FoldMI = foldMemoryOperand(MI, SrcOperandIds, *DefMI)) { + FoldAsLoadDefReg = 0; + return FoldMI; + } + + return nullptr; +} + +void Z80InstrInfo::updateOperandRegConstraints(MachineFunction &MF, + MachineInstr &NewMI) const { + MachineRegisterInfo &MRI = MF.getRegInfo(); + const TargetRegisterInfo &TRI = *MRI.getTargetRegisterInfo(); + + for (unsigned Idx : llvm::seq(0u, NewMI.getNumOperands())) { + MachineOperand &MO = NewMI.getOperand(Idx); + // We only need to update constraints on virtual register operands. + if (!MO.isReg()) + continue; + Register Reg = MO.getReg(); + if (!Reg.isVirtual()) + continue; + + auto *NewRC = + MRI.constrainRegClass(Reg, getRegClass(NewMI.getDesc(), Idx, &TRI, MF)); + if (!NewRC) { + LLVM_DEBUG( + dbgs() << "WARNING: Unable to update register constraint for operand " + << Idx << " of instruction:\n"; + NewMI.dump(); dbgs() << "\n"); + } + } +} + +MachineInstr *Z80InstrInfo::foldMemoryOperandImpl( + MachineFunction &MF, MachineInstr &MI, unsigned OpNum, + ArrayRef MOs, MachineBasicBlock::iterator InsertPt) const { + bool IsOff; + if (MOs.size() == 1 && MOs[0].isReg()) + IsOff = false; + else if (MOs.size() == 2 && (MOs[0].isReg() || MOs[0].isFI()) && + MOs[1].isImm()) + IsOff = true; + else + return nullptr; + + unsigned Opc; + switch (MI.getOpcode()) { + case Z80::BIT8bg: Opc = IsOff ? Z80::BIT8bo : Z80::BIT8bp; break; + case Z80::ADD8ar: Opc = IsOff ? Z80::ADD8ao : Z80::ADD8ap; break; + case Z80::ADC8ar: Opc = IsOff ? Z80::ADC8ao : Z80::ADC8ap; break; + case Z80::SUB8ar: Opc = IsOff ? Z80::SUB8ao : Z80::SUB8ap; break; + case Z80::SBC8ar: Opc = IsOff ? Z80::SBC8ao : Z80::SBC8ap; break; + case Z80::AND8ar: Opc = IsOff ? Z80::AND8ao : Z80::AND8ap; break; + case Z80::XOR8ar: Opc = IsOff ? Z80::XOR8ao : Z80::XOR8ap; break; + case Z80:: OR8ar: Opc = IsOff ? Z80:: OR8ao : Z80:: OR8ap; break; + case Z80::TST8ar: Opc = IsOff ? Z80::TST8ao : Z80::TST8ap; break; + default: return nullptr; + } + + MachineInstrBuilder MIB( + MF, MF.CreateMachineInstr(get(Opc), MI.getDebugLoc(), true)); + for (unsigned Idx : llvm::seq(0u, MI.getNumOperands())) { + MachineOperand &MO = MI.getOperand(Idx); + if (Idx == OpNum) { + assert(MO.isReg() && "Expected to fold into reg operand!"); + for (auto &AddrMO : MOs) + MIB.add(AddrMO); + } else + MIB.add(MO); + } + updateOperandRegConstraints(MF, *MIB); + InsertPt->getParent()->insert(InsertPt, MIB); + return MIB; +} + MachineInstr * Z80InstrInfo::foldMemoryOperandImpl(MachineFunction &MF, MachineInstr &MI, ArrayRef Ops, MachineBasicBlock::iterator InsertPt, int FrameIndex, LiveIntervals *LIS, VirtRegMap *VRM) const { - return nullptr; - bool Is24Bit = Subtarget.is24Bit(); - MachineBasicBlock &MBB = *InsertPt->getParent(); - if (Ops.size() == 1 && Ops[0] == 1 && MI.isFullCopy()) { - unsigned DstReg = MI.getOperand(0).getReg(); - if (Register::isPhysicalRegister(DstReg)) { - unsigned Opc; - if (Z80::R8RegClass.contains(DstReg)) { - Opc = Z80::LD8ro; - } else { - assert((Is24Bit ? Z80::R24RegClass : Z80::R16RegClass) - .contains(DstReg) && "Unexpected physical reg"); - Opc = Is24Bit ? Z80::LD24ro : Z80::LD16ro; - } - return BuildMI(MBB, InsertPt, MI.getDebugLoc(), get(Opc), DstReg) - .addFrameIndex(FrameIndex).addImm(0); - } - } - dbgs() << Ops.size() << ": "; - for (unsigned Op : Ops) - dbgs() << Op << ' '; - MI.dump(); - return nullptr; + for (auto Op : Ops) + if (MI.getOperand(Op).getSubReg()) + return nullptr; + + const MachineFrameInfo &MFI = MF.getFrameInfo(); + unsigned Size = MFI.getObjectSize(FrameIndex); + + if (Ops.size() != 1 || Size != 1) + return nullptr; + + MachineOperand MOs[2] = {MachineOperand::CreateFI(FrameIndex), + MachineOperand::CreateImm(0)}; + return foldMemoryOperandImpl(MF, MI, Ops[0], MOs, InsertPt); } + MachineInstr * Z80InstrInfo::foldMemoryOperandImpl(MachineFunction &MF, MachineInstr &MI, ArrayRef Ops, MachineBasicBlock::iterator InsertPt, MachineInstr &LoadMI, LiveIntervals *LIS) const { - return nullptr; - llvm_unreachable("Unimplemented"); + for (auto Op : Ops) + if (MI.getOperand(Op).getSubReg()) + return nullptr; + + int FrameIndex; + if (isLoadFromStackSlot(LoadMI, FrameIndex)) + return foldMemoryOperandImpl(MF, MI, Ops, InsertPt, FrameIndex, LIS); + + if (Ops.size() != 1) + return nullptr; + + auto MOs = LoadMI.explicit_uses(); + return foldMemoryOperandImpl(MF, MI, Ops[0], {MOs.begin(), MOs.end()}, + InsertPt); } diff --git a/llvm/lib/Target/Z80/Z80InstrInfo.h b/llvm/lib/Target/Z80/Z80InstrInfo.h index b163805953e553..d22bbb33e8fe21 100644 --- a/llvm/lib/Target/Z80/Z80InstrInfo.h +++ b/llvm/lib/Target/Z80/Z80InstrInfo.h @@ -162,24 +162,34 @@ class Z80InstrInfo final : public Z80GenInstrInfo { Register SrcReg2, int CmpMask, int CmpValue, const MachineRegisterInfo *MRI) const override; + MachineInstr *optimizeLoadInstr(MachineInstr &MI, + const MachineRegisterInfo *MRI, + unsigned &FoldAsLoadDefReg, + MachineInstr *&DefMI) const override; + /// Check whether the target can fold a load that feeds a subreg operand /// (or a subreg operand that feeds a store). bool isSubregFoldable() const override { return true; } - MachineInstr * - foldMemoryOperandImpl(MachineFunction &MF, MachineInstr &MI, - ArrayRef Ops, - MachineBasicBlock::iterator InsertPt, int FrameIndex, - LiveIntervals *LIS = nullptr, - VirtRegMap *VRM = nullptr) const override; - MachineInstr * - foldMemoryOperandImpl(MachineFunction &MF, MachineInstr &MI, - ArrayRef Ops, - MachineBasicBlock::iterator InsertPt, - MachineInstr &LoadMI, - LiveIntervals *LIS = nullptr) const override; + MachineInstr *foldMemoryOperandImpl(MachineFunction &MF, MachineInstr &MI, + ArrayRef Ops, + MachineBasicBlock::iterator InsertPt, + int FrameIndex, + LiveIntervals *LIS = nullptr, + VirtRegMap *VRM = nullptr) const override; + MachineInstr *foldMemoryOperandImpl( + MachineFunction &MF, MachineInstr &MI, ArrayRef Ops, + MachineBasicBlock::iterator InsertPt, MachineInstr &LoadMI, + LiveIntervals *LIS = nullptr) const override; private: + void updateOperandRegConstraints(MachineFunction &MF, + MachineInstr &NewMI) const; + MachineInstr * + foldMemoryOperandImpl(MachineFunction &MF, MachineInstr &MI, unsigned OpNum, + ArrayRef MOs, + MachineBasicBlock::iterator InsertPt) const; + /// canExchange - This returns whether the two instructions can be directly /// exchanged with one EX instruction. Since the only register exchange /// instruction is EX DE,HL, simply returns whether the two arguments are diff --git a/llvm/lib/Target/Z80/Z80InstrInfo.td b/llvm/lib/Target/Z80/Z80InstrInfo.td index 7845fbc32d59f1..9d45b8f617ac5a 100644 --- a/llvm/lib/Target/Z80/Z80InstrInfo.td +++ b/llvm/lib/Target/Z80/Z80InstrInfo.td @@ -822,8 +822,13 @@ defm CP : BinOp8F ; defm TST : BinOp8F , Requires<[HaveEZ80Ops]>; -def : Pat<(add R8:$reg, 1), (INC8r R8:$reg)>; -def : Pat<(add R8:$reg, -1), (DEC8r R8:$reg)>; +def : Pat<(fshl G8:$reg, G8:$reg, (i8 1)), (RLC8r G8:$reg)>; +def : Pat<(fshl G8:$reg, G8:$reg, (i8 7)), (RRC8r G8:$reg)>; +def : Pat<(shl G8:$reg, (i8 1)), (SLA8r G8:$reg)>; +def : Pat<(sra G8:$reg, (i8 1)), (SRA8r G8:$reg)>; +def : Pat<(srl G8:$reg, (i8 1)), (SRL8r G8:$reg)>; +def : Pat<(add R8:$reg, (i8 1)), (INC8r R8:$reg)>; +def : Pat<(add R8:$reg, (i8 -1)), (DEC8r R8:$reg)>; def INC16r : I16; def : Pat<(add R24:$imp, -1), (DEC24r R24:$imp)>; let Defs = [F] in { - def ADD16aa : I16; - def ADD24aa : I24; + let AddedComplexity = 1 in { + def ADD16aa : I16; + def ADD24aa : I24; + } def ADD16ao : I16; @@ -876,10 +883,14 @@ let Defs = [F] in { (outs A24:$dst), (ins A24:$imp), [(set A24:$dst, F, (Z80add_flag A24:$imp, SPL))]>; } -def : Pat<(add A16:$imp, A16:$imp), (ADD16aa A16:$imp)>; -def : Pat<(add A24:$imp, A24:$imp), (ADD24aa A24:$imp)>; -def : Pat<(addc A16:$imp, A16:$imp), (ADD16aa A16:$imp)>; -def : Pat<(addc A24:$imp, A24:$imp), (ADD24aa A24:$imp)>; +let AddedComplexity = 1 in { + def : Pat<(add A16:$imp, A16:$imp), (ADD16aa A16:$imp)>; + def : Pat<(add A24:$imp, A24:$imp), (ADD24aa A24:$imp)>; + def : Pat<(shl A16:$reg, (i8 1)), (ADD16aa A16:$reg)>; + def : Pat<(shl A24:$reg, (i8 1)), (ADD24aa A24:$reg)>; + def : Pat<(addc A16:$imp, A16:$imp), (ADD16aa A16:$imp)>; + def : Pat<(addc A24:$imp, A24:$imp), (ADD24aa A24:$imp)>; +} def : Pat<(add A16:$imp, O16:$src), (ADD16ao A16:$imp, O16:$src)>; def : Pat<(add A24:$imp, O24:$src), (ADD24ao A24:$imp, O24:$src)>; def : Pat<(addc A16:$imp, O16:$src), (ADD16ao A16:$imp, O16:$src)>; diff --git a/llvm/lib/Target/Z80/Z80MachineFunctionInfo.h b/llvm/lib/Target/Z80/Z80MachineFunctionInfo.h index 1efcd9c245d175..8179e013929188 100644 --- a/llvm/lib/Target/Z80/Z80MachineFunctionInfo.h +++ b/llvm/lib/Target/Z80/Z80MachineFunctionInfo.h @@ -21,6 +21,14 @@ namespace llvm { /// Z80MachineFunctionInfo - This class is derived from MachineFunction and /// contains private Z80 target-specific information for each MachineFunction. class Z80MachineFunctionInfo : public MachineFunctionInfo { +public: + enum AltFPMode { AFPM_None = 0, AFPM_Partial, AFPM_Full }; + +private: + /// Number of bytes of arguments this function has on the stack. All usable + /// during a tail call. + unsigned ArgFrameSize = 0; + /// CalleeSavedFrameSize - Size of the callee-saved register portion of the /// stack frame in bytes. unsigned CalleeSavedFrameSize = 0; @@ -38,11 +46,17 @@ class Z80MachineFunctionInfo : public MachineFunctionInfo { /// created. bool HasIllegalLEA = false; + /// UsesAltFP - We use an alternate frame pointer as an optimization. + AltFPMode UsesAltFP = AFPM_None; + public: Z80MachineFunctionInfo() = default; explicit Z80MachineFunctionInfo(MachineFunction &MF) {} + unsigned getArgFrameSize() const { return ArgFrameSize; } + void setArgFrameSize(unsigned Bytes) { ArgFrameSize = Bytes; } + unsigned getCalleeSavedFrameSize() const { return CalleeSavedFrameSize; } void setCalleeSavedFrameSize(unsigned Bytes) { CalleeSavedFrameSize = Bytes; } @@ -54,6 +68,9 @@ class Z80MachineFunctionInfo : public MachineFunctionInfo { bool getHasIllegalLEA() const { return HasIllegalLEA; } void setHasIllegalLEA(bool V = true) { HasIllegalLEA = V; } + + AltFPMode getUsesAltFP() const { return UsesAltFP; } + void setUsesAltFP(AltFPMode V) { UsesAltFP = V; } }; } // End llvm namespace diff --git a/llvm/lib/Target/Z80/Z80MachineLateOptimization.cpp b/llvm/lib/Target/Z80/Z80MachineLateOptimization.cpp new file mode 100644 index 00000000000000..aa3993aa0eb8b2 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80MachineLateOptimization.cpp @@ -0,0 +1,286 @@ +//=== lib/CodeGen/GlobalISel/Z80PreLegalizerCombiner.cpp ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This pass does combining of machine instructions at the generic MI level, +// before the legalizer. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "Z80.h" +#include "llvm/CodeGen/LiveRegUnits.h" +#include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/Constants.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "z80-machine-late-opt" + +using namespace llvm; + +namespace { +class ImmVal { + static constexpr unsigned UnknownOff = ~0u; + const GlobalValue *GV = nullptr; + unsigned Off = UnknownOff, Mask = 0; + MachineInstr *KilledBy = nullptr; + +public: + ImmVal() {} + ImmVal(MCRegister Reg, const TargetRegisterInfo &TRI) { + assert(Reg.isPhysical() && "Expected physical register"); + if (auto *RC = TRI.getMinimalPhysRegClass(Reg)) + Mask = maskTrailingOnes(TRI.getRegSizeInBits(*RC)); + } + ImmVal(MachineOperand &MO, MCRegister Reg, const TargetRegisterInfo &TRI) + : ImmVal(Reg, TRI) { + switch (MO.getType()) { + case MachineOperand::MO_Immediate: + Off = MO.getImm(); + break; + case MachineOperand::MO_CImmediate: + Off = MO.getCImm()->getZExtValue(); + break; + case MachineOperand::MO_GlobalAddress: + GV = MO.getGlobal(); + Off = MO.getOffset(); + break; + default: + return; + } + Off &= Mask; + assert(valid() && "Mask should have been less than 32 bits"); + } + ImmVal(unsigned Imm, MCRegister Reg, const TargetRegisterInfo &TRI) + : ImmVal(Reg, TRI) { + Off = Imm & Mask; + assert(valid() && "Mask should have been less than 32 bits"); + } + ImmVal(const ImmVal &SuperVal, unsigned Idx, const TargetRegisterInfo &TRI) { + Mask = maskTrailingOnes(TRI.getSubRegIdxSize(Idx)); + if (!SuperVal.isImm()) + return; + Off = SuperVal.Off >> TRI.getSubRegIdxOffset(Idx) & Mask; + assert(valid() && "Mask should have been less than 32 bits"); + } + + void clobber() { + Off = UnknownOff; + KilledBy = nullptr; + } + + bool valid() const { + return Off != UnknownOff; + } + bool isImm() const { + return valid() && !GV; + } + bool isGlobal() const { + return valid() && GV; + } + + bool match(ImmVal &Val, int Delta = 0) const { + return valid() && Val.valid() && GV == Val.GV && Mask == Val.Mask && + Off == ((Val.Off + Delta) & Mask); + } + + void setKilledBy(MachineInstr *MI) { + KilledBy = MI; + } + + void reuse(Register Reg, const TargetRegisterInfo &TRI) { + if (KilledBy) { + assert(KilledBy->killsRegister(Reg, &TRI) && + "KilledBy should kill register"); + KilledBy->clearRegisterKills(Reg, &TRI); + KilledBy = nullptr; + } + } + + friend raw_ostream &operator<<(raw_ostream &OS, ImmVal &Val) { + if (!Val.valid()) + return OS << "?"; + if (Val.GV) + OS << Val.GV << '+'; + return OS << Val.Off; + } +}; + +class Z80MachineLateOptimization : public MachineFunctionPass { + const TargetRegisterInfo *TRI; + ImmVal Vals[Z80::NUM_TARGET_REGS]; + + void clobberAll() { + for (unsigned Reg = 1; Reg != Z80::NUM_TARGET_REGS; ++Reg) + Vals[Reg] = ImmVal(Reg, *TRI); + } + template + void clobber(Register Reg, bool IncludeSelf) { + for (MCRegIterator I(Reg, TRI, IncludeSelf); I.isValid(); ++I) + Vals[*I] = ImmVal(*I, *TRI); + } + + bool tryReplaceReg(MachineRegisterInfo &MRI, MCRegister FromReg, + MCRegister ToReg); + void debug(const MachineInstr &MI); + +public: + static char ID; + + Z80MachineLateOptimization() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { + return "Z80 Machine Late Optimization"; + } + + bool runOnMachineFunction(MachineFunction &MF) override; +}; +} // end anonymous namespace + +bool Z80MachineLateOptimization::runOnMachineFunction(MachineFunction &MF) { + bool Changed = false; + TRI = MF.getSubtarget().getRegisterInfo(); + const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); + LiveRegUnits LiveUnits(*TRI); + for (MachineBasicBlock &MBB : MF) { + LiveUnits.clear(); + LiveUnits.addLiveIns(MBB); + clobberAll(); + for (MachineBasicBlock::iterator I = MBB.begin(), E = MBB.end(); I != E;) { + MachineInstr &MI = *I; + ++I; + LiveUnits.stepForward(MI); + switch (unsigned Opc = MI.getOpcode()) { + case Z80::LD8ri: + case Z80::LD16ri: + case Z80::LD24ri: + case Z80::LD24r0: + case Z80::LD24r_1: { + Register Reg = MI.getOperand(0).getReg(); + ImmVal Val; + switch (Opc) { + default: + Val = ImmVal(MI.getOperand(1), Reg, *TRI); + break; + case Z80::LD24r0: + Val = ImmVal(0, Reg, *TRI); + break; + case Z80::LD24r_1: + Val = ImmVal(-1, Reg, *TRI); + break; + } + if (Val.match(Vals[Reg])) { + LLVM_DEBUG(dbgs() << "Erasing redundant: "; MI.dump()); + MI.eraseFromParent(); + Vals[Reg].reuse(Reg, *TRI); + Changed = true; + continue; + } + if (Val.match(Vals[Reg], 1)) + switch (Opc) { + case Z80::LD8ri: + if (!LiveUnits.available(Z80::F)) + break; + Opc = Z80::INC8r; + break; + case Z80::LD16ri: + Opc = Z80::INC16r; + break; + case Z80::LD24ri: + case Z80::LD24r0: + case Z80::LD24r_1: + Opc = Z80::INC24r; + break; + } + else if (Val.match(Vals[Reg], -1)) + switch (Opc) { + case Z80::LD8ri: + if (!LiveUnits.available(Z80::F)) + break; + Opc = Z80::DEC8r; + break; + case Z80::LD16ri: + Opc = Z80::DEC16r; + break; + case Z80::LD24ri: + case Z80::LD24r0: + case Z80::LD24r_1: + Opc = Z80::DEC24r; + break; + } + if (Opc != MI.getOpcode()) { + LLVM_DEBUG(dbgs() << "Replacing: "; MI.dump();); + MI.setDesc(TII.get(Opc)); + MI.RemoveOperand(1); + MI.addImplicitDefUseOperands(MF); + Vals[Reg].reuse(Reg, *TRI); + LLVM_DEBUG(dbgs() << "With: "; MI.dump();); + } + clobber(Reg, false); + Vals[Reg] = Val; + for (MCSubRegIndexIterator SRII(Reg, TRI); SRII.isValid(); ++SRII) + Vals[SRII.getSubReg()] = ImmVal(Val, SRII.getSubRegIndex(), *TRI); + debug(MI); + continue; + } + case Z80::RLC8r: + case Z80::RRC8r: + case Z80::RL8r: + case Z80::RR8r: { + if (MI.getOperand(0).getReg() != Z80::A || !LiveUnits.available(Z80::F)) + break; + switch (Opc) { + case Z80::RLC8r: Opc = Z80::RLCA; break; + case Z80::RRC8r: Opc = Z80::RRCA; break; + case Z80::RL8r: Opc = Z80::RLA; break; + case Z80::RR8r: Opc = Z80::RRA; break; + } + LLVM_DEBUG(dbgs() << "Replacing: "; MI.dump(); dbgs() << " With: "); + MI.setDesc(TII.get(Opc)); + MI.getOperand(0).setImplicit(); + MI.getOperand(1).setImplicit(); + break; + } + } + for (MachineOperand &MO : MI.operands()) + if (MO.isReg() && MO.isKill() && MO.getReg().isPhysical()) + for (MCRegAliasIterator I(MO.getReg(), TRI, true); I.isValid(); ++I) + Vals[*I].setKilledBy(&MI); + for (MachineOperand &MO : MI.operands()) { + if (MO.isReg() && MO.isDef() && MO.getReg().isPhysical() && + !(MI.isCopy() && MO.isImplicit())) + clobber(MO.getReg(), true); + else if (MO.isRegMask()) + for (unsigned Reg = 1; Reg != Z80::NUM_TARGET_REGS; ++Reg) + if (MO.clobbersPhysReg(Reg)) + Vals[Reg] = ImmVal(Reg, *TRI); + } + debug(MI); + } + } + return Changed; +} + +void Z80MachineLateOptimization::debug(const MachineInstr &MI) { + for (unsigned Reg = 1; Reg != Z80::NUM_TARGET_REGS; ++Reg) + if (Vals[Reg].valid()) + LLVM_DEBUG(dbgs() << TRI->getName(Reg) << " = " << Vals[Reg] << ' '); + LLVM_DEBUG(MI.dump()); +} + +char Z80MachineLateOptimization::ID = 0; +INITIALIZE_PASS(Z80MachineLateOptimization, DEBUG_TYPE, + "Optimize Z80 machine instrs after regselect", false, false) + +namespace llvm { +FunctionPass *createZ80MachineLateOptimizationPass() { + return new Z80MachineLateOptimization(); +} +} // end namespace llvm diff --git a/llvm/lib/Target/Z80/Z80PostSelectCombiner.cpp b/llvm/lib/Target/Z80/Z80PostSelectCombiner.cpp new file mode 100644 index 00000000000000..78641ff0a30bc0 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80PostSelectCombiner.cpp @@ -0,0 +1,435 @@ +//=== lib/CodeGen/GlobalISel/Z80PostSelectCombiner.cpp --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This pass does combining of machine instructions at the generic MI level, +// before the legalizer. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "Z80.h" +#include "Z80InstrInfo.h" +#include "Z80Subtarget.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/InitializePasses.h" +#include "llvm/Support/Debug.h" +#include "llvm/Target/TargetMachine.h" + +#define DEBUG_TYPE "z80-postselect-combiner" + +using namespace llvm; + +static cl::opt CondCallThreshold("z80-cond-call-threshold", + cl::Hidden, cl::init(10)); + +namespace { +class Z80PostSelectCombiner : public MachineFunctionPass { +public: + static char ID; + + Z80PostSelectCombiner(); + + StringRef getPassName() const override { return "Z80 Post Select Combiner"; } + + bool runOnMachineFunction(MachineFunction &MF) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override; +}; + +class ValLoc { + Register Reg, Base; + int8_t Off; + + static Register getBaseReg(const MachineInstr &MI, unsigned OpNo) { + Register BaseReg; + const MachineOperand &BaseMO = MI.getOperand(OpNo); + if (BaseMO.isReg()) + BaseReg = BaseMO.getReg(); + else if (BaseMO.getIndex() >= 0) + return Register::index2StackSlot(BaseMO.getIndex()); + return BaseReg; + } + +public: + ValLoc() : Reg(), Base(), Off() {} + + ValLoc &setReg(Register Reg) { + this->Reg = Reg; + return *this; + } + ValLoc &setReg(const MachineInstr &MI, unsigned OpNo) { + return setReg(MI.getOperand(OpNo).getReg()); + } + ValLoc &setMem(Register Base, int8_t Off = 0) { + this->Base = Base; + this->Off = Off; + return *this; + } + ValLoc &setPtr(const MachineInstr &MI, unsigned OpNo) { + return setMem(MI.getOperand(OpNo).getReg()); + } + ValLoc &setOff(const MachineInstr &MI, unsigned OpNo) { + return setMem(getBaseReg(MI, OpNo), MI.getOperand(OpNo + 1).getImm()); + } + + bool matchesReg(Register Reg) const { + return Reg.isValid() && this->Reg == Reg; + } + bool matchesReg(const MachineInstr &MI, unsigned OpNo) const { + return matchesReg(MI.getOperand(OpNo).getReg()); + } + bool matchesMem(Register Base, int8_t Off = 0) const { + return Base.isValid() && this->Base == Base && this->Off == Off; + } + bool matchesPtr(const MachineInstr &MI, unsigned OpNo) const { + return matchesMem(MI.getOperand(OpNo).getReg()); + } + bool matchesOff(const MachineInstr &MI, unsigned OpNo) const { + return matchesMem(getBaseReg(MI, OpNo), MI.getOperand(OpNo + 1).getImm()); + } + + void clobberDefs(const MachineInstr &MI, const TargetRegisterInfo &TRI) { + for (const MachineOperand &DefMO : MI.defs()) + for (Register LocReg : {Reg, Base}) + if (LocReg.isValid() && TRI.regsOverlap(DefMO.getReg(), LocReg)) + return clear(); + } + + void clear() { + *this = ValLoc(); + } +}; + +} // end anonymous namespace + +void Z80PostSelectCombiner::getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesCFG(); + MachineFunctionPass::getAnalysisUsage(AU); +} + +Z80PostSelectCombiner::Z80PostSelectCombiner() : MachineFunctionPass(ID) { + initializeZ80PostSelectCombinerPass(*PassRegistry::getPassRegistry()); +} + +bool Z80PostSelectCombiner::runOnMachineFunction(MachineFunction &MF) { + MachineRegisterInfo &MRI = MF.getRegInfo(); + auto &STI = MF.getSubtarget(); + auto &TII = *STI.getInstrInfo(); + auto &TRI = *STI.getRegisterInfo(); + + bool Changed = false; + for (MachineBasicBlock &MBB : MF) { + auto I = MBB.begin(), E = MBB.end(); + ValLoc SZFlagLoc; + auto FlagLocs = {&SZFlagLoc}; + while (I != E) { + MachineInstr &MI = *I; + ++I; + + switch (unsigned Opc = MI.getOpcode()) { + case TargetOpcode::COPY: { + for (ValLoc *FlagLoc : FlagLocs) + if (FlagLoc->matchesReg(MI, 1)) + FlagLoc->setReg(MI, 0); + Register DstReg = MI.getOperand(0).getReg(); + if (DstReg != Z80::SPS && DstReg != Z80::SPL) + break; + Register TmpReg = + MRI.createVirtualRegister(DstReg == Z80::SPL ? &Z80::A24RegClass + : &Z80::A16RegClass); + BuildMI(MBB, MI, MI.getDebugLoc(), TII.get(TargetOpcode::COPY), TmpReg) + .add(MI.getOperand(1)); + MI.setDesc(TII.get(DstReg == Z80::SPL ? Z80::LD24SP : Z80::LD16SP)); + MI.getOperand(0).setReg(TmpReg); + MI.RemoveOperand(1); + Changed = true; + break; + } + case Z80::PUSH16r: + case Z80::PUSH24r: { + if (!STI.hasEZ80Ops()) + break; + bool IsPush24 = Opc == Z80::PUSH24r; + Register SrcReg = MI.getOperand(0).getReg(); + if (!MRI.hasOneUse(SrcReg)) + break; + MachineInstr *SrcMI = MRI.getVRegDef(SrcReg); + if (!SrcMI || + SrcMI->getOpcode() != (IsPush24 ? Z80::LEA24ro : Z80::LEA16ro)) + break; + MachineOperand &BaseMO = SrcMI->getOperand(1); + auto NewOff = SrcMI->getOperand(2).getImm(); + if (!BaseMO.isReg() || NewOff) { + MI.RemoveOperand(0); + MI.setDesc(TII.get(IsPush24 ? Z80::PEA24o : Z80::PEA16o)); + MachineInstrBuilder(MF, MI).add(SrcMI->getOperand(1)).addImm(NewOff); + } else + MI.getOperand(0).setReg(BaseMO.getReg()); + SrcMI->eraseFromParent(); + Changed = true; + break; + } + case Z80::LD8rp: + case Z80::LD8gp: + for (ValLoc *FlagLoc : FlagLocs) + if (FlagLoc->matchesPtr(MI, 1)) + FlagLoc->setReg(MI, 0); + break; + case Z80::LD8ro: + case Z80::LD8go: + for (ValLoc *FlagLoc : FlagLocs) + if (FlagLoc->matchesOff(MI, 1)) + FlagLoc->setReg(MI, 0); + break; + case Z80::LD8pr: + case Z80::LD8pg: + for (ValLoc *FlagLoc : FlagLocs) + if (FlagLoc->matchesReg(MI, 1)) + FlagLoc->setPtr(MI, 0); + break; + case Z80::LD8or: + case Z80::LD8og: + for (ValLoc *FlagLoc : FlagLocs) + if (FlagLoc->matchesReg(MI, 2)) + FlagLoc->setOff(MI, 0); + break; + case Z80::OR8ar: + if (MI.getOperand(0).getReg() == Z80::A && + SZFlagLoc.matchesReg(MI, 0)) { + MI.eraseFromParent(); + break; + } + LLVM_FALLTHROUGH; + case Z80::ADD8ar: + case Z80::ADD8ai: + case Z80::ADC8ar: + case Z80::ADC8ai: + case Z80::SUB8ar: + case Z80::SUB8ai: + case Z80::SBC8ar: + case Z80::SBC8ai: + case Z80::AND8ar: + case Z80::AND8ai: + case Z80::XOR8ar: + case Z80::XOR8ai: + case Z80::OR8ai: + SZFlagLoc.setReg(Z80::A); + break; + case Z80::RLC8r: + case Z80::RRC8r: + case Z80::RL8r: + case Z80::RR8r: + case Z80::SLA8r: + case Z80::SRA8r: + case Z80::SRL8r: + case Z80::INC8r: + case Z80::DEC8r: + SZFlagLoc.setReg(MI, 0); + break; + case Z80::ADD8ap: + case Z80::ADC8ap: + case Z80::SUB8ap: + case Z80::SBC8ap: + case Z80::AND8ap: + case Z80::XOR8ap: + case Z80::OR8ap: + case Z80::RLC8p: + case Z80::RRC8p: + case Z80::RL8p: + case Z80::RR8p: + case Z80::SLA8p: + case Z80::SRA8p: + case Z80::SRL8p: + case Z80::INC8p: + case Z80::DEC8p: + SZFlagLoc.setPtr(MI, 0); + break; + case Z80::ADD8ao: + case Z80::ADC8ao: + case Z80::SUB8ao: + case Z80::SBC8ao: + case Z80::AND8ao: + case Z80::XOR8ao: + case Z80::OR8ao: + case Z80::RLC8o: + case Z80::RRC8o: + case Z80::RL8o: + case Z80::RR8o: + case Z80::SLA8o: + case Z80::SRA8o: + case Z80::SRL8o: + case Z80::INC8o: + case Z80::DEC8o: + SZFlagLoc.setOff(MI, 0); + break; + default: + if (MI.modifiesRegister(Z80::F, &TRI)) + for (ValLoc *FlagLoc : FlagLocs) + FlagLoc->clear(); + break; + } + + for (ValLoc *FlagLoc : FlagLocs) + FlagLoc->clobberDefs(MI, TRI); + } + } + + for (MachineBasicBlock &MBB : MF) { + MachineBasicBlock *TrueMBB = nullptr, *FalseMBB = nullptr; + SmallVector Cond; + if (TII.analyzeBranch(MBB, TrueMBB, FalseMBB, Cond, false) || Cond.empty()) + continue; + if (!FalseMBB) + FalseMBB = &*std::next(MBB.getIterator()); + assert(TrueMBB && FalseMBB && "Expected to be nonnull"); + for (int I = 0; I != 2; ++I) { + if (TrueMBB->succ_empty() && TrueMBB->isReturnBlock()) { + auto II = TrueMBB->begin(); + while (II->isCopy() || II->isMetaInstruction()) + ++II; + if (++II == TrueMBB->end()) { + // Unimplemented until FPE works. + //Changed = true; + } + } + if (TII.reverseBranchCondition(Cond)) + break; + std::swap(TrueMBB, FalseMBB); + } + // Separate loop because we want to prefer the above optimization. + for (int I = 0; I != 2; ++I) { + if (TrueMBB->pred_size() == 1 && TrueMBB->succ_size() == 1 && + TrueMBB->isSuccessor(FalseMBB)) { + MachineBasicBlock::iterator I = TrueMBB->begin(); + MachineBasicBlock::iterator E = TrueMBB->getFirstTerminator(); + if (I != E && TII.isFrameSetup(*I) && TII.isFrameInstr(*--E) && + I != E) { + unsigned Cost = 0; + MachineInstr *CallMI = nullptr; + struct Result { + Register PhysReg, TrueReg, FalseReg, ResReg; + }; + SmallVector Results; + while (++I != E) { + ++Cost; + unsigned Opc = I->getOpcode(); + if (Opc == Z80::CALL16 || Opc == Z80::CALL24) { + if (CallMI) { + CallMI = nullptr; + break; + } + CallMI = &*I; + } + if (TII.isFrameSetup(*I) || + (!CallMI && I->modifiesRegister(Z80::F, &TRI))) { + CallMI = nullptr; + break; + } + if (CallMI && Opc == TargetOpcode::COPY) { + if (Results.size() == 4) { + CallMI = nullptr; + break; + } + Result &Res = Results.emplace_back(); + Res.TrueReg = I->getOperand(0).getReg(); + Res.PhysReg = I->getOperand(1).getReg(); + assert(Res.TrueReg.isVirtual() && Res.PhysReg.isPhysical() && + "Expected phys to virt reg copy inside call sequence"); + } + } + for (I = FalseMBB->begin(), E = FalseMBB->end(); CallMI && I != E && I->isPHI(); + ++I) { + if (I->getNumOperands() != 5) { + CallMI = nullptr; + break; + } + Register FalseReg, TrueReg; + for (unsigned OpNo = 1; CallMI && OpNo != 5; OpNo += 2) { + Register Reg = I->getOperand(OpNo).getReg(); + MachineBasicBlock &PredMBB = *I->getOperand(OpNo + 1).getMBB(); + if (&PredMBB == &MBB) + FalseReg = Reg; + else if (&PredMBB == TrueMBB) + TrueReg = Reg; + else + CallMI = nullptr; + } + bool Found = false; + for (Result &Res : Results) { + if (Res.TrueReg != TrueReg) + continue; + if (Res.FalseReg.isValid()) + break; + Res.FalseReg = FalseReg; + Res.ResReg = I->getOperand(0).getReg(); + Found = true; + break; + } + if (!Found) + CallMI = nullptr; + } + for (Result &Res : Results) { + if (!Res.FalseReg.isValid()) { + CallMI = nullptr; + break; + } + } + if (CallMI && Cost < CondCallThreshold) { + Register TempReg = MRI.createVirtualRegister(&Z80::F8RegClass); + DebugLoc DL = MBB.findBranchDebugLoc(); + MBB.removeSuccessor(FalseMBB); + TII.removeBranch(MBB); + BuildMI(&MBB, DL, TII.get(TargetOpcode::COPY), TempReg) + .addReg(Z80::F); + if (!MBB.isLayoutSuccessor(TrueMBB)) + TII.insertUnconditionalBranch(MBB, TrueMBB, DL); + BuildMI(*TrueMBB, TrueMBB->begin(), DL, TII.get(TargetOpcode::COPY), + Z80::F).addReg(TempReg); + CallMI->setDesc(TII.get(CallMI->getOpcode() == Z80::CALL24 + ? Z80::CALL24CC : Z80::CALL16CC)); + auto RegMask = CallMI->getOperand(1).getRegMask(); + CallMI->RemoveOperand(1); + MachineInstrBuilder(MF, CallMI) + .add(Cond[0]).addRegMask(RegMask) + .addReg(Z80::F, RegState::Implicit); + for (Result &Res : Results) { + BuildMI(*TrueMBB, CallMI, CallMI->getDebugLoc(), + TII.get(TargetOpcode::COPY), Res.PhysReg) + .addReg(Res.FalseReg); + CallMI->addRegisterKilled(Res.PhysReg, &TRI, true); + } + Changed = true; + } + } + } + if (TII.reverseBranchCondition(Cond)) + break; + std::swap(TrueMBB, FalseMBB); + } + } + + return Changed; +} + +char Z80PostSelectCombiner::ID = 0; +INITIALIZE_PASS_BEGIN(Z80PostSelectCombiner, DEBUG_TYPE, + "Combine Z80 machine instrs after inst selection", false, + false) +INITIALIZE_PASS_DEPENDENCY(TargetPassConfig); +INITIALIZE_PASS_DEPENDENCY(InstructionSelect); +INITIALIZE_PASS_END(Z80PostSelectCombiner, DEBUG_TYPE, + "Combine Z80 machine instrs after inst selection", false, + false) + + +namespace llvm { +FunctionPass *createZ80PostSelectCombiner() { + return new Z80PostSelectCombiner; +} +} // end namespace llvm diff --git a/llvm/lib/Target/Z80/Z80RegisterInfo.cpp b/llvm/lib/Target/Z80/Z80RegisterInfo.cpp index 1127c6455169a1..83993cfd8bf3ac 100644 --- a/llvm/lib/Target/Z80/Z80RegisterInfo.cpp +++ b/llvm/lib/Target/Z80/Z80RegisterInfo.cpp @@ -185,6 +185,7 @@ void Z80RegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, unsigned Opc = MI.getOpcode(); MachineBasicBlock &MBB = *MI.getParent(); MachineFunction &MF = *MBB.getParent(); + auto &FuncInfo = *MF.getInfo(); const Z80Subtarget &STI = MF.getSubtarget(); const Z80InstrInfo &TII = *STI.getInstrInfo(); const Z80FrameLowering *TFI = getFrameLowering(MF); @@ -197,8 +198,7 @@ void Z80RegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, int BaseOff = MF.getFrameInfo().getObjectOffset(FrameIndex); int SlotSize = Is24Bit ? 3 : 2; // Skip any saved callee saved registers - if (TFI->hasFP(MF)) - BaseOff += SlotSize; + BaseOff += FuncInfo.getCalleeSavedFrameSize(); // Skip return address for arguments if (FrameIndex < 0) BaseOff += SlotSize; @@ -206,7 +206,11 @@ void Z80RegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, if (isFrameOffsetLegal(&MI, BasePtr, BaseOff) && (Opc != Z80::LEA16ro || STI.hasEZ80Ops())) { MI.getOperand(FIOperandNum).ChangeToRegister(BasePtr, false); - MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Off); + if (!Off && (Opc == Z80::PEA24o || Opc == Z80::PEA16o)) { + MI.setDesc(TII.get(Opc == Z80::PEA24o ? Z80::PUSH24r : Z80::PUSH16r)); + MI.RemoveOperand(FIOperandNum + 1); + } else + MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Off); return; } bool SaveFlags = RS->isRegUsed(Z80::F); @@ -321,8 +325,11 @@ void Z80RegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, } Register Z80RegisterInfo::getFrameRegister(const MachineFunction &MF) const { - return getFrameLowering(MF)->hasFP(MF) ? (Is24Bit ? Z80::UIX : Z80::IX) - : (Is24Bit ? Z80::SPL : Z80::SPS); + return getFrameLowering(MF)->hasFP(MF) + ? MF.getInfo()->getUsesAltFP() + ? Is24Bit ? Z80::UIY : Z80::IY + : Is24Bit ? Z80::UIX : Z80::IX + : Is24Bit ? Z80::SPL : Z80::SPS; } bool Z80RegisterInfo:: diff --git a/llvm/lib/Target/Z80/Z80RegisterInfo.td b/llvm/lib/Target/Z80/Z80RegisterInfo.td index 5f5f2716044c30..ec26ccc0848619 100644 --- a/llvm/lib/Target/Z80/Z80RegisterInfo.td +++ b/llvm/lib/Target/Z80/Z80RegisterInfo.td @@ -119,7 +119,7 @@ def I24 : Z80RC24<(add UIY, UIX)>; def A24 : Z80RC24<(add UHL, I24)>; def R24 : Z80RC24<(add G24, I24)>; let CopyCost = -1 in -def Z24 : Z80RC24<(add SPL)>; +def Z24 : Z80RC24<(add SPL, PC)>; //def S24 : Z80RC24<(add R24, AF)>; //def L24 : Z80RC24<(add G24, I24)>; //def R24 : Z80RC24<(add L24, SPL)>; diff --git a/llvm/lib/Target/Z80/Z80TargetMachine.cpp b/llvm/lib/Target/Z80/Z80TargetMachine.cpp index 47cccd11e4656f..c5b1331efc1d48 100644 --- a/llvm/lib/Target/Z80/Z80TargetMachine.cpp +++ b/llvm/lib/Target/Z80/Z80TargetMachine.cpp @@ -37,7 +37,10 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeZ80Target() { RegisterTargetMachine Y(getTheEZ80Target()); PassRegistry &PR = *PassRegistry::getPassRegistry(); + initializeZ80PreLegalizerCombinerPass(PR); initializeGlobalISel(PR); + initializeZ80PostSelectCombinerPass(PR); + initializeZ80MachineLateOptimizationPass(PR); } static std::string computeDataLayout(const Triple &TT) { @@ -129,10 +132,15 @@ class Z80PassConfig : public TargetPassConfig { } bool addIRTranslator() override; + void addPreLegalizeMachineIR() override; bool addLegalizeMachineIR() override; bool addRegBankSelect() override; bool addGlobalInstructionSelect() override; + void addMachineSSAOptimization() override; void addFastRegAlloc() override; + void addMachineLateOptimization() override; + + std::unique_ptr getCSEConfig() const override; }; } // end anonymous namespace @@ -145,6 +153,11 @@ bool Z80PassConfig::addIRTranslator() { return false; } +void Z80PassConfig::addPreLegalizeMachineIR() { + bool IsOptNone = getOptLevel() == CodeGenOpt::None; + addPass(createZ80PreLegalizeCombiner(IsOptNone)); +} + bool Z80PassConfig::addLegalizeMachineIR() { addPass(new Legalizer); return false; @@ -160,6 +173,11 @@ bool Z80PassConfig::addGlobalInstructionSelect() { return false; } +void Z80PassConfig::addMachineSSAOptimization() { + addPass(createZ80PostSelectCombiner()); + TargetPassConfig::addMachineSSAOptimization(); +} + void Z80PassConfig::addFastRegAlloc() { // FastRegAlloc can't handle the register pressure on the Z80 if (usingDefaultRegAlloc()) @@ -167,3 +185,11 @@ void Z80PassConfig::addFastRegAlloc() { else TargetPassConfig::addFastRegAlloc(); } + +void Z80PassConfig::addMachineLateOptimization() { + addPass(createZ80MachineLateOptimizationPass()); +} + +std::unique_ptr Z80PassConfig::getCSEConfig() const { + return getStandardCSEConfigForOpt(TM->getOptLevel()); +} From 3c720394ddea000e1268b90bf1d9110265eac66b Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 15 Jan 2020 23:31:43 -0500 Subject: [PATCH 16/21] [Z80] Change assembler syntax from zds to fasmg-ez80. --- llvm/include/llvm/MC/MCAsmInfo.h | 20 ++++++++++++ llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 4 +++ llvm/lib/MC/MCAsmInfo.cpp | 3 ++ llvm/lib/MC/MCAsmInfoOMF.cpp | 2 +- llvm/lib/MC/MCAsmInfoXCOFF.cpp | 1 + llvm/lib/MC/MCAsmStreamer.cpp | 10 +++--- llvm/lib/MC/MCObjectFileInfo.cpp | 8 ++--- llvm/lib/MC/MCSectionOMF.cpp | 2 +- llvm/lib/MC/MCStreamer.cpp | 3 +- .../Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp | 31 +++++++++++-------- .../Z80/MCTargetDesc/Z80TargetStreamer.cpp | 15 ++++++--- .../Z80/MCTargetDesc/Z80TargetStreamer.h | 4 +++ llvm/lib/Target/Z80/Z80AsmPrinter.cpp | 6 ++-- 13 files changed, 79 insertions(+), 30 deletions(-) diff --git a/llvm/include/llvm/MC/MCAsmInfo.h b/llvm/include/llvm/MC/MCAsmInfo.h index 42234ee56f11a9..3b78109e5922c5 100644 --- a/llvm/include/llvm/MC/MCAsmInfo.h +++ b/llvm/include/llvm/MC/MCAsmInfo.h @@ -191,6 +191,10 @@ class MCAsmInfo { /// used to emit these bytes. Defaults to true. bool ZeroDirectiveSupportsNonZeroValue = true; + /// This should be set to the separator used in block directives between + /// the number of bytes and the fill value. Defaults to ", ". + const char *BlockSeparator; + /// This directive allows emission of an ascii string with the standard C /// escape characters embedded into it. If a target doesn't support this, it /// can be set to null. Defaults to "\t.ascii\t" @@ -229,6 +233,8 @@ class MCAsmInfo { const char *TPRel32Directive = nullptr; const char *TPRel64Directive = nullptr; + bool AlwaysChangeSection = false; + /// This is true if this target uses "Sun Style" syntax for section switching /// ("#alloc,#write" etc) instead of the normal ELF syntax (,"a,w") in /// .section directives. Defaults to false. @@ -259,6 +265,15 @@ class MCAsmInfo { /// ".globl". const char *GlobalDirective; + /// This is the directive used to declare a local entity. Defaults to + /// nothing. + const char *LGloblDirective = nullptr; + + /// This is the directive used to assign a symbol. Defaults to ".set " and + /// ", ". + const char *SetDirective; + const char *SetSeparator; + /// True if the expression /// .long f - g /// uses a relocation but it can be suppressed by writing @@ -486,6 +501,7 @@ class MCAsmInfo { /// returns false => .section .text,#alloc,#execinstr /// returns true => .text virtual bool shouldOmitSectionDirective(StringRef SectionName) const; + bool shouldAlwaysChangeSection() const { return AlwaysChangeSection; } bool usesSunStyleELFSectionSwitchSyntax() const { return SunStyleELFSectionSwitchSyntax; @@ -565,12 +581,16 @@ class MCAsmInfo { return ZeroDirectiveSupportsNonZeroValue; } virtual const char *getBlockDirective(int64_t Size) const { return nullptr; } + const char *getBlockSeparator() const { return BlockSeparator; } const char *getAsciiDirective() const { return AsciiDirective; } const char *getAscizDirective() const { return AscizDirective; } bool getAlignmentIsInBytes() const { return AlignmentIsInBytes; } unsigned getTextAlignFillValue() const { return TextAlignFillValue; } const char *getGlobalDirective() const { return GlobalDirective; } + const char *getLGloblDirective() const { return LGloblDirective; } + const char *getSetDirective() const { return SetDirective; } + const char *getSetSeparator() const { return SetSeparator; } bool doesSetDirectiveSuppressReloc() const { return SetDirectiveSuppressesReloc; diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 1540a19687c5fb..f15706d0a9b9b9 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -422,6 +422,8 @@ void AsmPrinter::emitLinkage(const GlobalValue *GV, MCSymbol *GVSym) const { return; case GlobalValue::PrivateLinkage: case GlobalValue::InternalLinkage: + if (MAI->getLGloblDirective()) + OutStreamer->emitSymbolAttribute(GVSym, MCSA_LGlobal); return; case GlobalValue::ExternalWeakLinkage: case GlobalValue::AvailableExternallyLinkage: @@ -1960,6 +1962,8 @@ void AsmPrinter::emitJumpTableInfo() { OutStreamer->emitLabel(GetJTISymbol(JTI, true)); MCSymbol* JTISymbol = GetJTISymbol(JTI); + if (MAI->getLGloblDirective()) + OutStreamer->emitSymbolAttribute(JTISymbol, MCSA_LGlobal); OutStreamer->emitLabel(JTISymbol); for (unsigned ii = 0, ee = JTBBs.size(); ii != ee; ++ii) diff --git a/llvm/lib/MC/MCAsmInfo.cpp b/llvm/lib/MC/MCAsmInfo.cpp index 3184c218a1bb3a..fa678b08a43d2b 100644 --- a/llvm/lib/MC/MCAsmInfo.cpp +++ b/llvm/lib/MC/MCAsmInfo.cpp @@ -42,6 +42,7 @@ MCAsmInfo::MCAsmInfo() { Code32Directive = ".code32"; Code64Directive = ".code64"; ZeroDirective = "\t.zero\t"; + BlockSeparator = ", "; AsciiDirective = "\t.ascii\t"; AscizDirective = "\t.asciz\t"; Data8bitsDirective = "\t.byte\t"; @@ -50,6 +51,8 @@ MCAsmInfo::MCAsmInfo() { Data32bitsDirective = "\t.long\t"; Data64bitsDirective = "\t.quad\t"; GlobalDirective = "\t.globl\t"; + SetDirective = ".set "; + SetSeparator = ", "; WeakDirective = "\t.weak\t"; if (DwarfExtendedLoc != Default) SupportsExtendedDwarfLocDirective = DwarfExtendedLoc == Enable; diff --git a/llvm/lib/MC/MCAsmInfoOMF.cpp b/llvm/lib/MC/MCAsmInfoOMF.cpp index 74a55e2ee3fce2..39f173d67a9758 100644 --- a/llvm/lib/MC/MCAsmInfoOMF.cpp +++ b/llvm/lib/MC/MCAsmInfoOMF.cpp @@ -19,5 +19,5 @@ using namespace llvm; void MCAsmInfoOMF::anchor() {} MCAsmInfoOMF::MCAsmInfoOMF() { - PrivateLabelPrefix = "L"; + PrivateLabelPrefix = "?"; } diff --git a/llvm/lib/MC/MCAsmInfoXCOFF.cpp b/llvm/lib/MC/MCAsmInfoXCOFF.cpp index 1531f61da95e74..b278184a707f1b 100644 --- a/llvm/lib/MC/MCAsmInfoXCOFF.cpp +++ b/llvm/lib/MC/MCAsmInfoXCOFF.cpp @@ -32,6 +32,7 @@ MCAsmInfoXCOFF::MCAsmInfoXCOFF() { COMMDirectiveAlignmentIsInBytes = false; LCOMMDirectiveAlignmentType = LCOMM::Log2Alignment; HasDotTypeDotSizeDirective = false; + LGloblDirective = "\t.lglobl\t"; SymbolsHaveSMC = true; UseIntegratedAssembler = false; NeedsFunctionDescriptors = true; diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index 700937e854ada3..7b62590fd370c9 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -612,9 +612,9 @@ void MCAsmStreamer::emitAssignment(MCSymbol *Symbol, const MCExpr *Value) { if (E->inlineAssignedExpr()) EmitSet = false; if (EmitSet) { - OS << ".set "; + OS << MAI->getSetDirective(); Symbol->print(OS, MAI); - OS << ", "; + OS << MAI->getSetSeparator(); Value->print(OS, MAI); EmitEOL(); @@ -662,7 +662,9 @@ bool MCAsmStreamer::emitSymbolAttribute(MCSymbol *Symbol, case MCSA_Global: // .globl/.global OS << MAI->getGlobalDirective(); break; - case MCSA_LGlobal: OS << "\t.lglobl\t"; break; + case MCSA_LGlobal: // .lglobl + OS << MAI->getLGloblDirective(); + break; case MCSA_Hidden: OS << "\t.hidden\t"; break; case MCSA_IndirectSymbol: OS << "\t.indirect_symbol\t"; break; case MCSA_Internal: OS << "\t.internal\t"; break; @@ -1177,7 +1179,7 @@ void MCAsmStreamer::emitFill(const MCExpr &NumBytes, uint64_t FillValue, if (const char *BlockDirective = MAI->getBlockDirective(1)) { OS << BlockDirective; NumBytes.print(OS, MAI); - OS << ", " << FillValue; + OS << MAI->getBlockSeparator() << FillValue; EmitEOL(); return; } diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp index f4210071a3622b..c90d191f95ab94 100644 --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -860,10 +860,10 @@ void MCObjectFileInfo::initXCOFFMCObjectFileInfo(const Triple &T) { } void MCObjectFileInfo::initOMFMCObjectFileInfo(const Triple &T) { - TextSection = Ctx->getOMFSection("CODE", SectionKind::getText()); - DataSection = Ctx->getOMFSection("DATA", SectionKind::getData()); - BSSSection = Ctx->getOMFSection("BSS", SectionKind::getBSS()); - ReadOnlySection = Ctx->getOMFSection("TEXT", SectionKind::getReadOnly()); + TextSection = Ctx->getOMFSection(".text", SectionKind::getText()); + DataSection = Ctx->getOMFSection(".data", SectionKind::getData()); + BSSSection = Ctx->getOMFSection(".bss", SectionKind::getBSS()); + ReadOnlySection = Ctx->getOMFSection(".rodata", SectionKind::getReadOnly()); } void MCObjectFileInfo::InitMCObjectFileInfo(const Triple &TheTriple, bool PIC, diff --git a/llvm/lib/MC/MCSectionOMF.cpp b/llvm/lib/MC/MCSectionOMF.cpp index 8c7b5670bef85f..617abf40d0b76f 100644 --- a/llvm/lib/MC/MCSectionOMF.cpp +++ b/llvm/lib/MC/MCSectionOMF.cpp @@ -23,5 +23,5 @@ void MCSectionOMF::PrintSwitchToSection(const MCAsmInfo &MAI, const Triple &T, raw_ostream &OS, const MCExpr *Subsection) const { assert(!Subsection && "Unimplemented!"); - OS << "\tSEGMENT\t" << getName() << '\n'; + OS << "\tsection\t" << getName() << '\n'; } diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp index 0b650ef936bffb..531135cef5645d 100644 --- a/llvm/lib/MC/MCStreamer.cpp +++ b/llvm/lib/MC/MCStreamer.cpp @@ -1113,7 +1113,8 @@ void MCStreamer::SwitchSection(MCSection *Section, const MCExpr *Subsection) { MCSymbol *Sym = Section->getBeginSymbol(); if (Sym && !Sym->isInSection()) emitLabel(Sym); - } + } else if (Context.getAsmInfo()->shouldAlwaysChangeSection()) + changeSection(Section, Subsection); } MCSymbol *MCStreamer::endSection(MCSection *Section) { diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp index f99ff291ede0c7..078f5e148d69d6 100644 --- a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp @@ -26,32 +26,37 @@ Z80MCAsmInfo::Z80MCAsmInfo(const Triple &T) { SeparatorString = nullptr; CommentString = ";"; PrivateGlobalPrefix = PrivateLabelPrefix = ""; - Code16Directive = ".assume\tadl = 0"; - Code24Directive = ".assume\tadl = 1"; + Code16Directive = "assume\tadl = 0"; + Code24Directive = "assume\tadl = 1"; Code32Directive = Code64Directive = nullptr; AssemblerDialect = !Is16Bit; SupportsQuotedNames = false; ZeroDirective = AsciiDirective = AscizDirective = nullptr; - Data8bitsDirective = "\tDB\t"; - Data16bitsDirective = "\tDW\t"; - Data24bitsDirective = "\tDW24\t"; - Data32bitsDirective = "\tDL\t"; + BlockSeparator = " dup "; + Data8bitsDirective = "\tdb\t"; + Data16bitsDirective = "\tdw\t"; + Data24bitsDirective = "\tdl\t"; + Data32bitsDirective = "\tdd\t"; Data64bitsDirective = nullptr; - GlobalDirective = "\tXDEF\t"; + AlwaysChangeSection = true; + GlobalDirective = "\tpublic\t"; + LGloblDirective = "\tprivate\t"; + SetDirective = "\tlabel\t"; + SetSeparator = " at "; HasFunctionAlignment = false; HasDotTypeDotSizeDirective = false; - WeakDirective = nullptr; + WeakDirective = "\tweak\t"; UseIntegratedAssembler = false; - WeakDirective = nullptr; UseLogicalShr = false; + HasSingleParameterDotFile = false; } const char *Z80MCAsmInfo::getBlockDirective(int64_t Size) const { switch (Size) { default: return nullptr; - case 1: return "\tBLKB\t"; - case 2: return "\tBLKW\t"; - case 3: return "\tBLKP\t"; - case 4: return "\tBLKL\t"; + case 1: return "\tdb\t"; + case 2: return "\tdw\t"; + case 3: return "\tdl\t"; + case 4: return "\tdd\t"; } } diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.cpp index 6bdcf98ea057ec..d86d2049952d59 100644 --- a/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.cpp +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.cpp @@ -26,22 +26,29 @@ Z80TargetAsmStreamer::Z80TargetAsmStreamer(MCStreamer &S, void Z80TargetAsmStreamer::emitAlign(unsigned ByteAlignment) { if (ByteAlignment > 1) - OS << "\tALIGN\t" << ByteAlignment << '\n'; + OS << "\trb\t" << (ByteAlignment - 1) << " - ($ - $$ + " + << (ByteAlignment - 1) << ") mod " << ByteAlignment << "\n"; } void Z80TargetAsmStreamer::emitBlock(uint64_t NumBytes) { if (NumBytes) - OS << "\tDS\t" << NumBytes << '\n'; + OS << "\trb\t" << NumBytes << '\n'; +} + +void Z80TargetAsmStreamer::emitLocal(MCSymbol *Symbol) { + OS << "\tprivate\t"; + Symbol->print(OS, MAI); + OS << '\n'; } void Z80TargetAsmStreamer::emitGlobal(MCSymbol *Symbol) { - OS << "\tXDEF\t"; + OS << "\tpublic\t"; Symbol->print(OS, MAI); OS << '\n'; } void Z80TargetAsmStreamer::emitExtern(MCSymbol *Symbol) { - OS << "\tXREF\t"; + OS << "\textern\t"; Symbol->print(OS, MAI); OS << '\n'; } diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.h index af7b93c56efd65..f3b757593fef0d 100644 --- a/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.h +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.h @@ -30,6 +30,9 @@ class Z80TargetStreamer : public MCTargetStreamer { // .block virtual void emitBlock(uint64_t NumBytes) = 0; + // .private + virtual void emitLocal(MCSymbol *Symbol) = 0; + // .global virtual void emitGlobal(MCSymbol *Symbol) = 0; @@ -46,6 +49,7 @@ class Z80TargetAsmStreamer final : public Z80TargetStreamer { void emitAlign(unsigned ByteAlignment) override; void emitBlock(uint64_t NumBytes) override; + void emitLocal(MCSymbol *Symbol) override; void emitGlobal(MCSymbol *Symbol) override; void emitExtern(MCSymbol *Symbol) override; }; diff --git a/llvm/lib/Target/Z80/Z80AsmPrinter.cpp b/llvm/lib/Target/Z80/Z80AsmPrinter.cpp index 3464183685722e..d8c3c19239a7bd 100644 --- a/llvm/lib/Target/Z80/Z80AsmPrinter.cpp +++ b/llvm/lib/Target/Z80/Z80AsmPrinter.cpp @@ -13,9 +13,9 @@ //===----------------------------------------------------------------------===// #include "Z80AsmPrinter.h" -#include "Z80.h" #include "MCTargetDesc/Z80MCTargetDesc.h" #include "MCTargetDesc/Z80TargetStreamer.h" +#include "Z80.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCStreamer.h" #include "llvm/Support/TargetRegistry.h" @@ -87,7 +87,9 @@ void Z80AsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { OutStreamer->SwitchSection(TheSection); TS->emitAlign(Align); - if (!GV->hasLocalLinkage()) + if (GV->hasLocalLinkage()) + TS->emitLocal(GVSym); + else TS->emitGlobal(GVSym); OutStreamer->emitLabel(GVSym); if (GVKind.isBSS()) From 3732596ff448768b4b702b513386d297deb1bbfc Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 1 Nov 2019 03:10:37 +0100 Subject: [PATCH 17/21] [Z80] Add Z80 tests. --- clang/include/clang/Basic/TargetOptions.h | 3 + clang/include/clang/Driver/CC1Options.td | 7 + clang/lib/Basic/Targets/X86.h | 11 + clang/lib/Frontend/CompilerInvocation.cpp | 1 + llvm/test/CodeGen/Z80/compare16.ll | 1858 +++++++++++++++++++++ llvm/test/CodeGen/Z80/compare24.ll | 724 ++++++++ llvm/test/CodeGen/Z80/compare8.ll | 1578 +++++++++++++++++ llvm/test/CodeGen/Z80/intrinsics.ll | 371 ++++ llvm/test/CodeGen/Z80/intrinsics24.ll | 101 ++ llvm/test/CodeGen/Z80/ops.ll | 778 +++++++++ llvm/test/CodeGen/Z80/ops24.ll | 46 + llvm/utils/UpdateTestChecks/asm.py | 22 + 12 files changed, 5500 insertions(+) create mode 100644 llvm/test/CodeGen/Z80/compare16.ll create mode 100644 llvm/test/CodeGen/Z80/compare24.ll create mode 100644 llvm/test/CodeGen/Z80/compare8.ll create mode 100644 llvm/test/CodeGen/Z80/intrinsics.ll create mode 100644 llvm/test/CodeGen/Z80/intrinsics24.ll create mode 100644 llvm/test/CodeGen/Z80/ops.ll create mode 100644 llvm/test/CodeGen/Z80/ops24.ll diff --git a/clang/include/clang/Basic/TargetOptions.h b/clang/include/clang/Basic/TargetOptions.h index bbe86aebb0741c..0e2456d69abcc3 100644 --- a/clang/include/clang/Basic/TargetOptions.h +++ b/clang/include/clang/Basic/TargetOptions.h @@ -81,6 +81,9 @@ class TargetOptions { /// * CUDA compilation uses it to control parts of CUDA compilation /// in clang that depend on specific version of the CUDA SDK. llvm::VersionTuple SDKVersion; + + /// Very specific hack for testing the ez80 backend. + bool TestEZ80Hack; }; } // end namespace clang diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td index 8729512454c3ad..9f3c49e507f8c7 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -948,3 +948,10 @@ def dwarf_debug_producer : Separate<["-"], "dwarf-debug-producer">, def defsym : Separate<["-"], "defsym">, HelpText<"Define a value for a symbol">; } // let Flags = [CC1AsOption] + +//===----------------------------------------------------------------------===// +// Temp Debug Options +//===----------------------------------------------------------------------===// +// Temp Debug Options +def test_ez80_hack : Flag<["-"], "test-ez80-hack">, + Flags<[CC1Option]>, HelpText<"Enable hack for testing ez80 code generation.">; diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h index c33c608e27c843..e19550aaa1a471 100644 --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -668,6 +668,17 @@ class LLVM_LIBRARY_VISIBILITY X86_64TargetInfo : public X86TargetInfo { // x86-64 has atomics up to 16 bytes. MaxAtomicPromoteWidth = 128; MaxAtomicInlineWidth = 64; + + if (Opts.TestEZ80Hack) { + // ez80 debug hack + IntWidth = 24; + LongWidth = LongAlign = 32; + SizeType = UnsignedLongLong; + PtrDiffType = SignedLongLong; + IntPtrType = SignedLongLong; + IntMaxType = SignedLongLong; + Int64Type = SignedLongLong; + } } BuiltinVaListKind getBuiltinVaListKind() const override { diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 8bd248c9503063..6b59fb002467b6 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3660,6 +3660,7 @@ static void ParseTargetArgs(TargetOptions &Opts, ArgList &Args, else Opts.SDKVersion = Version; } + Opts.TestEZ80Hack = Args.hasArg(OPT_test_ez80_hack); } bool CompilerInvocation::parseSimpleArgs(const ArgList &Args, diff --git a/llvm/test/CodeGen/Z80/compare16.ll b/llvm/test/CodeGen/Z80/compare16.ll new file mode 100644 index 00000000000000..e1f5ada7ae1f3d --- /dev/null +++ b/llvm/test/CodeGen/Z80/compare16.ll @@ -0,0 +1,1858 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=z80 < %s | FileCheck %s --check-prefixes=Z80 +; RUN: llc -mtriple=ez80-code16 < %s | FileCheck %s --check-prefixes=Z80-CODE16 +; RUN: llc -mtriple=ez80 < %s | FileCheck %s --check-prefixes=EZ80 + +declare void @external() + +define void @icmp.eq.i16(i16, i16) { +; Z80-LABEL: icmp.eq.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld e, (ix + 6) +; Z80-NEXT: ld d, (ix + 7) +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call z, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.eq.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, (ix + 6) +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call z, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.eq.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld e, (ix + 9) +; EZ80-NEXT: ld d, (ix + 10) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.eq.i16.0(i16) { +; Z80-LABEL: icmp.eq.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: add hl, bc +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, bc +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call z, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.eq.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: add hl, bc +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, bc +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call z, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.eq.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: add.sis hl, bc +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, bc +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.eq.i16.64(i16) { +; Z80-LABEL: icmp.eq.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 64 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call z, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.eq.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 64 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call z, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.eq.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 64 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ne.i16(i16, i16) { +; Z80-LABEL: icmp.ne.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld e, (ix + 6) +; Z80-NEXT: ld d, (ix + 7) +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nz, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ne.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, (ix + 6) +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nz, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ne.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld e, (ix + 9) +; EZ80-NEXT: ld d, (ix + 10) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ne.i16.0(i16) { +; Z80-LABEL: icmp.ne.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: add hl, bc +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, bc +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nz, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ne.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: add hl, bc +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, bc +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nz, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ne.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: add.sis hl, bc +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, bc +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ne.i16.64(i16) { +; Z80-LABEL: icmp.ne.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 64 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nz, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ne.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 64 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nz, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ne.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 64 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ult.i16(i16, i16) { +; Z80-LABEL: icmp.ult.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld e, (ix + 6) +; Z80-NEXT: ld d, (ix + 7) +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ult.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, (ix + 6) +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ult.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld e, (ix + 9) +; EZ80-NEXT: ld d, (ix + 10) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ult.i16.0(i16) { +; Z80-LABEL: icmp.ult.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 0 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ult.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 0 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ult.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 0 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ult.i16.64(i16) { +; Z80-LABEL: icmp.ult.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 64 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ult.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 64 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ult.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 64 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ule.i16(i16, i16) { +; Z80-LABEL: icmp.ule.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld e, (ix + 4) +; Z80-NEXT: ld d, (ix + 5) +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: ld h, (ix + 7) +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ule.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld de, (ix + 4) +; Z80-CODE16-NEXT: ld hl, (ix + 6) +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ule.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld e, (ix + 6) +; EZ80-NEXT: ld d, (ix + 7) +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: ld h, (ix + 10) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ule.i16.0(i16) { +; Z80-LABEL: icmp.ule.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 1 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ule.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 1 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ule.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 1 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ule.i16.64(i16) { +; Z80-LABEL: icmp.ule.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 65 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ule.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 65 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ule.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 65 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ugt.i16(i16, i16) { +; Z80-LABEL: icmp.ugt.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld e, (ix + 4) +; Z80-NEXT: ld d, (ix + 5) +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: ld h, (ix + 7) +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ugt.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld de, (ix + 4) +; Z80-CODE16-NEXT: ld hl, (ix + 6) +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ugt.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld e, (ix + 6) +; EZ80-NEXT: ld d, (ix + 7) +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: ld h, (ix + 10) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ugt.i16.0(i16) { +; Z80-LABEL: icmp.ugt.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 1 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ugt.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 1 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ugt.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 1 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ugt.i16.64(i16) { +; Z80-LABEL: icmp.ugt.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 65 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ugt.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 65 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ugt.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 65 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.uge.i16(i16, i16) { +; Z80-LABEL: icmp.uge.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld e, (ix + 6) +; Z80-NEXT: ld d, (ix + 7) +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.uge.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, (ix + 6) +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.uge.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld e, (ix + 9) +; EZ80-NEXT: ld d, (ix + 10) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.uge.i16.0(i16) { +; Z80-LABEL: icmp.uge.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 0 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.uge.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 0 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.uge.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 0 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.uge.i16.64(i16) { +; Z80-LABEL: icmp.uge.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 64 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.uge.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 64 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.uge.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 64 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.slt.i16(i16, i16) { +; Z80-LABEL: icmp.slt.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: push hl +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: ld h, (ix + 7) +; Z80-NEXT: ex (sp), hl +; Z80-NEXT: pop iy +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add iy, de +; Z80-NEXT: add hl, de +; Z80-NEXT: ld e, iyl +; Z80-NEXT: ld d, iyh +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.slt.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld iy, (ix + 6) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add iy, de +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: lea de, iy + 0 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.slt.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: push hl +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: ld h, (ix + 10) +; EZ80-NEXT: ex (sp), hl +; EZ80-NEXT: pop iy +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis iy, de +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: ld e, iyl +; EZ80-NEXT: ld d, iyh +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.slt.i16.0(i16) { +; Z80-LABEL: icmp.slt.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add hl, de +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.slt.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.slt.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.slt.i16.64(i16) { +; Z80-LABEL: icmp.slt.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add hl, de +; Z80-NEXT: ld de, -32704 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.slt.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: ld de, -32704 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.slt.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: ld.sis de, -32704 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sle.i16(i16, i16) { +; Z80-LABEL: icmp.sle.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: push hl +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ex (sp), hl +; Z80-NEXT: pop iy +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: ld h, (ix + 7) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add iy, de +; Z80-NEXT: add hl, de +; Z80-NEXT: ld e, iyl +; Z80-NEXT: ld d, iyh +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sle.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld iy, (ix + 4) +; Z80-CODE16-NEXT: ld hl, (ix + 6) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add iy, de +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: lea de, iy + 0 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sle.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: push hl +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ex (sp), hl +; EZ80-NEXT: pop iy +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: ld h, (ix + 10) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis iy, de +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: ld e, iyl +; EZ80-NEXT: ld d, iyh +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sle.i16.0(i16) { +; Z80-LABEL: icmp.sle.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add hl, de +; Z80-NEXT: inc de +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sle.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: inc de +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sle.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: inc.sis de +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sle.i16.64(i16) { +; Z80-LABEL: icmp.sle.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add hl, de +; Z80-NEXT: ld de, -32703 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sle.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: ld de, -32703 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sle.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: ld.sis de, -32703 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sgt.i16(i16, i16) { +; Z80-LABEL: icmp.sgt.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: push hl +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ex (sp), hl +; Z80-NEXT: pop iy +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: ld h, (ix + 7) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add iy, de +; Z80-NEXT: add hl, de +; Z80-NEXT: ld e, iyl +; Z80-NEXT: ld d, iyh +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sgt.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld iy, (ix + 4) +; Z80-CODE16-NEXT: ld hl, (ix + 6) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add iy, de +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: lea de, iy + 0 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sgt.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: push hl +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ex (sp), hl +; EZ80-NEXT: pop iy +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: ld h, (ix + 10) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis iy, de +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: ld e, iyl +; EZ80-NEXT: ld d, iyh +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sgt.i16.0(i16) { +; Z80-LABEL: icmp.sgt.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add hl, de +; Z80-NEXT: inc de +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sgt.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: inc de +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sgt.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: inc.sis de +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sgt.i16.64(i16) { +; Z80-LABEL: icmp.sgt.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add hl, de +; Z80-NEXT: ld de, -32703 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sgt.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: ld de, -32703 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sgt.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: ld.sis de, -32703 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sge.i16(i16, i16) { +; Z80-LABEL: icmp.sge.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: push hl +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: ld h, (ix + 7) +; Z80-NEXT: ex (sp), hl +; Z80-NEXT: pop iy +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add iy, de +; Z80-NEXT: add hl, de +; Z80-NEXT: ld e, iyl +; Z80-NEXT: ld d, iyh +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sge.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld iy, (ix + 6) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add iy, de +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: lea de, iy + 0 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sge.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: push hl +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: ld h, (ix + 10) +; EZ80-NEXT: ex (sp), hl +; EZ80-NEXT: pop iy +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis iy, de +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: ld e, iyl +; EZ80-NEXT: ld d, iyh +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sge.i16.0(i16) { +; Z80-LABEL: icmp.sge.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add hl, de +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sge.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sge.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sge.i16.64(i16) { +; Z80-LABEL: icmp.sge.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add hl, de +; Z80-NEXT: ld de, -32704 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sge.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: ld de, -32704 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sge.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: ld.sis de, -32704 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} diff --git a/llvm/test/CodeGen/Z80/compare24.ll b/llvm/test/CodeGen/Z80/compare24.ll new file mode 100644 index 00000000000000..2d22a6e61ed2d9 --- /dev/null +++ b/llvm/test/CodeGen/Z80/compare24.ll @@ -0,0 +1,724 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=ez80 < %s | FileCheck %s --check-prefixes=EZ80 + +declare void @external() + +define void @icmp.eq.i24(i24, i24) { +; EZ80-LABEL: icmp.eq.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, (ix + 9) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.eq.i24.0(i24) { +; EZ80-LABEL: icmp.eq.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: add hl, bc +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, bc +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.eq.i24.64(i24) { +; EZ80-LABEL: icmp.eq.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 64 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ne.i24(i24, i24) { +; EZ80-LABEL: icmp.ne.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, (ix + 9) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ne.i24.0(i24) { +; EZ80-LABEL: icmp.ne.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: add hl, bc +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, bc +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ne.i24.64(i24) { +; EZ80-LABEL: icmp.ne.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 64 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ult.i24(i24, i24) { +; EZ80-LABEL: icmp.ult.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, (ix + 9) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ult.i24.0(i24) { +; EZ80-LABEL: icmp.ult.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 0 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ult.i24.64(i24) { +; EZ80-LABEL: icmp.ult.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 64 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ule.i24(i24, i24) { +; EZ80-LABEL: icmp.ule.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld de, (ix + 6) +; EZ80-NEXT: ld hl, (ix + 9) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ule.i24.0(i24) { +; EZ80-LABEL: icmp.ule.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 1 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ule.i24.64(i24) { +; EZ80-LABEL: icmp.ule.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 65 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ugt.i24(i24, i24) { +; EZ80-LABEL: icmp.ugt.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld de, (ix + 6) +; EZ80-NEXT: ld hl, (ix + 9) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ugt.i24.0(i24) { +; EZ80-LABEL: icmp.ugt.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 1 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ugt.i24.64(i24) { +; EZ80-LABEL: icmp.ugt.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 65 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.uge.i24(i24, i24) { +; EZ80-LABEL: icmp.uge.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, (ix + 9) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.uge.i24.0(i24) { +; EZ80-LABEL: icmp.uge.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 0 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.uge.i24.64(i24) { +; EZ80-LABEL: icmp.uge.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 64 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.slt.i24(i24, i24) { +; EZ80-LABEL: icmp.slt.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld iy, (ix + 9) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add iy, de +; EZ80-NEXT: add hl, de +; EZ80-NEXT: lea de, iy + 0 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.slt.i24.0(i24) { +; EZ80-LABEL: icmp.slt.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add hl, de +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.slt.i24.64(i24) { +; EZ80-LABEL: icmp.slt.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add hl, de +; EZ80-NEXT: ld de, -8388544 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sle.i24(i24, i24) { +; EZ80-LABEL: icmp.sle.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld iy, (ix + 6) +; EZ80-NEXT: ld hl, (ix + 9) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add iy, de +; EZ80-NEXT: add hl, de +; EZ80-NEXT: lea de, iy + 0 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sle.i24.0(i24) { +; EZ80-LABEL: icmp.sle.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add hl, de +; EZ80-NEXT: inc de +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sle.i24.64(i24) { +; EZ80-LABEL: icmp.sle.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add hl, de +; EZ80-NEXT: ld de, -8388543 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sgt.i24(i24, i24) { +; EZ80-LABEL: icmp.sgt.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld iy, (ix + 6) +; EZ80-NEXT: ld hl, (ix + 9) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add iy, de +; EZ80-NEXT: add hl, de +; EZ80-NEXT: lea de, iy + 0 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sgt.i24.0(i24) { +; EZ80-LABEL: icmp.sgt.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add hl, de +; EZ80-NEXT: inc de +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sgt.i24.64(i24) { +; EZ80-LABEL: icmp.sgt.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add hl, de +; EZ80-NEXT: ld de, -8388543 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sge.i24(i24, i24) { +; EZ80-LABEL: icmp.sge.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld iy, (ix + 9) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add iy, de +; EZ80-NEXT: add hl, de +; EZ80-NEXT: lea de, iy + 0 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sge.i24.0(i24) { +; EZ80-LABEL: icmp.sge.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add hl, de +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sge.i24.64(i24) { +; EZ80-LABEL: icmp.sge.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add hl, de +; EZ80-NEXT: ld de, -8388544 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} diff --git a/llvm/test/CodeGen/Z80/compare8.ll b/llvm/test/CodeGen/Z80/compare8.ll new file mode 100644 index 00000000000000..36c66e5074905f --- /dev/null +++ b/llvm/test/CodeGen/Z80/compare8.ll @@ -0,0 +1,1578 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=z80 < %s | FileCheck %s --check-prefixes=Z80 +; RUN: llc -mtriple=ez80-code16 < %s | FileCheck %s --check-prefixes=Z80-CODE16 +; RUN: llc -mtriple=ez80 < %s | FileCheck %s --check-prefixes=EZ80 + +declare void @external() + +define void @icmp.eq.i8(i8, i8) { +; Z80-LABEL: icmp.eq.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: cp a, l +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call z, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.eq.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: ld l, (ix + 6) +; Z80-CODE16-NEXT: cp a, l +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call z, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.eq.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: cp a, l +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.eq.i8.0(i8) { +; Z80-LABEL: icmp.eq.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: or a, a +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call z, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.eq.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call z, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.eq.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: or a, a +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.eq.i8.64(i8) { +; Z80-LABEL: icmp.eq.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 64 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call z, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.eq.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 64 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call z, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.eq.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 64 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ne.i8(i8, i8) { +; Z80-LABEL: icmp.ne.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: cp a, l +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nz, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ne.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: ld l, (ix + 6) +; Z80-CODE16-NEXT: cp a, l +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nz, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ne.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: cp a, l +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ne.i8.0(i8) { +; Z80-LABEL: icmp.ne.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: or a, a +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nz, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ne.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nz, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ne.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: or a, a +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ne.i8.64(i8) { +; Z80-LABEL: icmp.ne.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 64 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nz, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ne.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 64 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nz, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ne.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 64 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ult.i8(i8, i8) { +; Z80-LABEL: icmp.ult.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: cp a, l +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ult.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: ld l, (ix + 6) +; Z80-CODE16-NEXT: cp a, l +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ult.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: cp a, l +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ult.i8.0(i8) { +; Z80-LABEL: icmp.ult.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 0 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ult.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 0 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ult.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 0 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ult.i8.64(i8) { +; Z80-LABEL: icmp.ult.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 64 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ult.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 64 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ult.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 64 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ule.i8(i8, i8) { +; Z80-LABEL: icmp.ule.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld a, (ix + 6) +; Z80-NEXT: cp a, l +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ule.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld l, (ix + 4) +; Z80-CODE16-NEXT: ld a, (ix + 6) +; Z80-CODE16-NEXT: cp a, l +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ule.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld a, (ix + 9) +; EZ80-NEXT: cp a, l +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ule.i8.0(i8) { +; Z80-LABEL: icmp.ule.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 1 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ule.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 1 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ule.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 1 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ule.i8.64(i8) { +; Z80-LABEL: icmp.ule.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 65 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ule.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 65 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ule.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 65 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ugt.i8(i8, i8) { +; Z80-LABEL: icmp.ugt.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld a, (ix + 6) +; Z80-NEXT: cp a, l +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ugt.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld l, (ix + 4) +; Z80-CODE16-NEXT: ld a, (ix + 6) +; Z80-CODE16-NEXT: cp a, l +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ugt.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld a, (ix + 9) +; EZ80-NEXT: cp a, l +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ugt.i8.0(i8) { +; Z80-LABEL: icmp.ugt.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 1 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ugt.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 1 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ugt.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 1 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ugt.i8.64(i8) { +; Z80-LABEL: icmp.ugt.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 65 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ugt.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 65 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ugt.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 65 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.uge.i8(i8, i8) { +; Z80-LABEL: icmp.uge.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: cp a, l +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.uge.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: ld l, (ix + 6) +; Z80-CODE16-NEXT: cp a, l +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.uge.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: cp a, l +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.uge.i8.0(i8) { +; Z80-LABEL: icmp.uge.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 0 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.uge.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 0 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.uge.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 0 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.uge.i8.64(i8) { +; Z80-LABEL: icmp.uge.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 64 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.uge.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 64 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.uge.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 64 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.slt.i8(i8, i8) { +; Z80-LABEL: icmp.slt.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld a, (ix + 6) +; Z80-NEXT: add a, -128 +; Z80-NEXT: ld e, a +; Z80-NEXT: ld a, l +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, e +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.slt.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld l, (ix + 4) +; Z80-CODE16-NEXT: ld a, (ix + 6) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: ld e, a +; Z80-CODE16-NEXT: ld a, l +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, e +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.slt.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld a, (ix + 9) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: ld e, a +; EZ80-NEXT: ld a, l +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, e +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.slt.i8.0(i8) { +; Z80-LABEL: icmp.slt.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, -128 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.slt.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, -128 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.slt.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, -128 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.slt.i8.64(i8) { +; Z80-LABEL: icmp.slt.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, -64 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.slt.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, -64 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.slt.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, -64 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sle.i8(i8, i8) { +; Z80-LABEL: icmp.sle.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: add a, -128 +; Z80-NEXT: ld e, a +; Z80-NEXT: ld a, l +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, e +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sle.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: ld l, (ix + 6) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: ld e, a +; Z80-CODE16-NEXT: ld a, l +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, e +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sle.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: ld e, a +; EZ80-NEXT: ld a, l +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, e +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sle.i8.0(i8) { +; Z80-LABEL: icmp.sle.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, -127 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sle.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, -127 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sle.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, -127 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sle.i8.64(i8) { +; Z80-LABEL: icmp.sle.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, -63 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sle.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, -63 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sle.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, -63 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sgt.i8(i8, i8) { +; Z80-LABEL: icmp.sgt.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: add a, -128 +; Z80-NEXT: ld e, a +; Z80-NEXT: ld a, l +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, e +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sgt.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: ld l, (ix + 6) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: ld e, a +; Z80-CODE16-NEXT: ld a, l +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, e +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sgt.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: ld e, a +; EZ80-NEXT: ld a, l +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, e +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sgt.i8.0(i8) { +; Z80-LABEL: icmp.sgt.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, -127 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sgt.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, -127 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sgt.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, -127 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sgt.i8.64(i8) { +; Z80-LABEL: icmp.sgt.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, -63 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sgt.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, -63 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sgt.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, -63 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sge.i8(i8, i8) { +; Z80-LABEL: icmp.sge.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld a, (ix + 6) +; Z80-NEXT: add a, -128 +; Z80-NEXT: ld e, a +; Z80-NEXT: ld a, l +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, e +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sge.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld l, (ix + 4) +; Z80-CODE16-NEXT: ld a, (ix + 6) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: ld e, a +; Z80-CODE16-NEXT: ld a, l +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, e +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sge.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld a, (ix + 9) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: ld e, a +; EZ80-NEXT: ld a, l +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, e +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sge.i8.0(i8) { +; Z80-LABEL: icmp.sge.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, -128 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sge.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, -128 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sge.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, -128 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sge.i8.64(i8) { +; Z80-LABEL: icmp.sge.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, -64 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sge.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, -64 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sge.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, -64 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} diff --git a/llvm/test/CodeGen/Z80/intrinsics.ll b/llvm/test/CodeGen/Z80/intrinsics.ll new file mode 100644 index 00000000000000..b675ed70569d51 --- /dev/null +++ b/llvm/test/CodeGen/Z80/intrinsics.ll @@ -0,0 +1,371 @@ +; RUN: llc -mtriple=z80 < %s +; RUN: llc -mtriple=ez80-code16 < %s +; RUN: llc -mtriple=ez80 < %s + +declare i8 @llvm.bitreverse.i8(i8) +define i8 @bitreverse.i8(i8) { + call i8 @llvm.bitreverse.i8(i8 %0) + ret i8 %2 +} +declare i16 @llvm.bitreverse.i16(i16) +define i16 @bitreverse.i16(i16) { + call i16 @llvm.bitreverse.i16(i16 %0) + ret i16 %2 +} +declare i32 @llvm.bitreverse.i32(i32) +define i32 @bitreverse.i32(i32) { + call i32 @llvm.bitreverse.i32(i32 %0) + ret i32 %2 +} +declare i64 @llvm.bitreverse.i64(i64) +define i64 @bitreverse.i64(i64) { + call i64 @llvm.bitreverse.i64(i64 %0) + ret i64 %2 +} + +declare i16 @llvm.bswap.i16(i16) +define i16 @bswap.i16(i16) { + call i16 @llvm.bswap.i16(i16 %0) + ret i16 %2 +} +declare i32 @llvm.bswap.i32(i32) +define i32 @bswap.i32(i32) { + call i32 @llvm.bswap.i32(i32 %0) + ret i32 %2 +} +declare i64 @llvm.bswap.i64(i64) +define i64 @bswap.i64(i64) { + call i64 @llvm.bswap.i64(i64 %0) + ret i64 %2 +} + +declare i8 @llvm.ctpop.i8(i8) +define i8 @ctpop.i8(i8) { + call i8 @llvm.ctpop.i8(i8 %0) + ret i8 %2 +} +declare i16 @llvm.ctpop.i16(i16) +define i16 @ctpop.i16(i16) { + call i16 @llvm.ctpop.i16(i16 %0) + ret i16 %2 +} +declare i32 @llvm.ctpop.i32(i32) +define i32 @ctpop.i32(i32) { + call i32 @llvm.ctpop.i32(i32 %0) + ret i32 %2 +} +declare i64 @llvm.ctpop.i64(i64) +define i64 @ctpop.i64(i64) { + call i64 @llvm.ctpop.i64(i64 %0) + ret i64 %2 +} + +declare i8 @llvm.ctlz.i8(i8) +define i8 @ctlz.i8(i8) { + call i8 @llvm.ctlz.i8(i8 %0) + ret i8 %2 +} +declare i16 @llvm.ctlz.i16(i16) +define i16 @ctlz.i16(i16) { + call i16 @llvm.ctlz.i16(i16 %0) + ret i16 %2 +} +declare i32 @llvm.ctlz.i32(i32) +define i32 @ctlz.i32(i32) { + call i32 @llvm.ctlz.i32(i32 %0) + ret i32 %2 +} +declare i64 @llvm.ctlz.i64(i64) +define i64 @ctlz.i64(i64) { + call i64 @llvm.ctlz.i64(i64 %0) + ret i64 %2 +} + +declare i8 @llvm.cttz.i8(i8) +define i8 @cttz.i8(i8) { + call i8 @llvm.cttz.i8(i8 %0) + ret i8 %2 +} +declare i16 @llvm.cttz.i16(i16) +define i16 @cttz.i16(i16) { + call i16 @llvm.cttz.i16(i16 %0) + ret i16 %2 +} +declare i32 @llvm.cttz.i32(i32) +define i32 @cttz.i32(i32) { + call i32 @llvm.cttz.i32(i32 %0) + ret i32 %2 +} +declare i64 @llvm.cttz.i64(i64) +define i64 @cttz.i64(i64) { + call i64 @llvm.cttz.i64(i64 %0) + ret i64 %2 +} + +declare i8 @llvm.fshl.i8(i8, i8, i8) +define i8 @fshl.i8(i8, i8, i8) { + call i8 @llvm.fshl.i8(i8 %0, i8 %1, i8 %2) + ret i8 %4 +} +declare i16 @llvm.fshl.i16(i16, i16, i16) +define i16 @fshl.i16(i16, i16, i16) { + call i16 @llvm.fshl.i16(i16 %0, i16 %1, i16 %2) + ret i16 %4 +} +declare i32 @llvm.fshl.i32(i32, i32, i32) +define i32 @fshl.i32(i32, i32, i32) { + call i32 @llvm.fshl.i32(i32 %0, i32 %1, i32 %2) + ret i32 %4 +} +declare i64 @llvm.fshl.i64(i64, i64, i64) +define i64 @fshl.i64(i64, i64, i64) { + call i64 @llvm.fshl.i64(i64 %0, i64 %1, i64 %2) + ret i64 %4 +} + +declare i8 @llvm.fshr.i8(i8, i8, i8) +define i8 @fshr.i8(i8, i8, i8) { + call i8 @llvm.fshr.i8(i8 %0, i8 %1, i8 %2) + ret i8 %4 +} +declare i16 @llvm.fshr.i16(i16, i16, i16) +define i16 @fshr.i16(i16, i16, i16) { + call i16 @llvm.fshr.i16(i16 %0, i16 %1, i16 %2) + ret i16 %4 +} +declare i32 @llvm.fshr.i32(i32, i32, i32) +define i32 @fshr.i32(i32, i32, i32) { + call i32 @llvm.fshr.i32(i32 %0, i32 %1, i32 %2) + ret i32 %4 +} +declare i64 @llvm.fshr.i64(i64, i64, i64) +define i64 @fshr.i64(i64, i64, i64) { + call i64 @llvm.fshr.i64(i64 %0, i64 %1, i64 %2) + ret i64 %4 +} + +declare {i8, i1} @llvm.sadd.with.overflow.i8(i8, i8) +define i1 @sadd.with.overflow.i8(i8, i8) { + call {i8, i1} @llvm.sadd.with.overflow.i8(i8 %0, i8 %1) + extractvalue {i8, i1} %3, 1 + ret i1 %4 +} +declare {i16, i1} @llvm.sadd.with.overflow.i16(i16, i16) +define i1 @sadd.with.overflow.i16(i16, i16) { + call {i16, i1} @llvm.sadd.with.overflow.i16(i16 %0, i16 %1) + extractvalue {i16, i1} %3, 1 + ret i1 %4 +} +declare {i32, i1} @llvm.sadd.with.overflow.i32(i32, i32) +define i1 @sadd.with.overflow.i32(i32, i32) { + call {i32, i1} @llvm.sadd.with.overflow.i32(i32 %0, i32 %1) + extractvalue {i32, i1} %3, 1 + ret i1 %4 +} +declare {i64, i1} @llvm.sadd.with.overflow.i64(i64, i64) +define i1 @sadd.with.overflow.i64(i64, i64) { + call {i64, i1} @llvm.sadd.with.overflow.i64(i64 %0, i64 %1) + extractvalue {i64, i1} %3, 1 + ret i1 %4 +} + +declare {i8, i1} @llvm.uadd.with.overflow.i8(i8, i8) +define i1 @uadd.with.overflow.i8(i8, i8) { + call {i8, i1} @llvm.uadd.with.overflow.i8(i8 %0, i8 %1) + extractvalue {i8, i1} %3, 1 + ret i1 %4 +} +declare {i16, i1} @llvm.uadd.with.overflow.i16(i16, i16) +define i1 @uadd.with.overflow.i16(i16, i16) { + call {i16, i1} @llvm.uadd.with.overflow.i16(i16 %0, i16 %1) + extractvalue {i16, i1} %3, 1 + ret i1 %4 +} +declare {i32, i1} @llvm.uadd.with.overflow.i32(i32, i32) +define i1 @uadd.with.overflow.i32(i32, i32) { + call {i32, i1} @llvm.uadd.with.overflow.i32(i32 %0, i32 %1) + extractvalue {i32, i1} %3, 1 + ret i1 %4 +} +declare {i64, i1} @llvm.uadd.with.overflow.i64(i64, i64) +define i1 @uadd.with.overflow.i64(i64, i64) { + call {i64, i1} @llvm.uadd.with.overflow.i64(i64 %0, i64 %1) + extractvalue {i64, i1} %3, 1 + ret i1 %4 +} + +declare {i8, i1} @llvm.ssub.with.overflow.i8(i8, i8) +define i1 @ssub.with.overflow.i8(i8, i8) { + call {i8, i1} @llvm.ssub.with.overflow.i8(i8 %0, i8 %1) + extractvalue {i8, i1} %3, 1 + ret i1 %4 +} +declare {i16, i1} @llvm.ssub.with.overflow.i16(i16, i16) +define i1 @ssub.with.overflow.i16(i16, i16) { + call {i16, i1} @llvm.ssub.with.overflow.i16(i16 %0, i16 %1) + extractvalue {i16, i1} %3, 1 + ret i1 %4 +} +declare {i32, i1} @llvm.ssub.with.overflow.i32(i32, i32) +define i1 @ssub.with.overflow.i32(i32, i32) { + call {i32, i1} @llvm.ssub.with.overflow.i32(i32 %0, i32 %1) + extractvalue {i32, i1} %3, 1 + ret i1 %4 +} +declare {i64, i1} @llvm.ssub.with.overflow.i64(i64, i64) +define i1 @ssub.with.overflow.i64(i64, i64) { + call {i64, i1} @llvm.ssub.with.overflow.i64(i64 %0, i64 %1) + extractvalue {i64, i1} %3, 1 + ret i1 %4 +} + +declare {i8, i1} @llvm.usub.with.overflow.i8(i8, i8) +define i1 @usub.with.overflow.i8(i8, i8) { + call {i8, i1} @llvm.usub.with.overflow.i8(i8 %0, i8 %1) + extractvalue {i8, i1} %3, 1 + ret i1 %4 +} +declare {i16, i1} @llvm.usub.with.overflow.i16(i16, i16) +define i1 @usub.with.overflow.i16(i16, i16) { + call {i16, i1} @llvm.usub.with.overflow.i16(i16 %0, i16 %1) + extractvalue {i16, i1} %3, 1 + ret i1 %4 +} +declare {i32, i1} @llvm.usub.with.overflow.i32(i32, i32) +define i1 @usub.with.overflow.i32(i32, i32) { + call {i32, i1} @llvm.usub.with.overflow.i32(i32 %0, i32 %1) + extractvalue {i32, i1} %3, 1 + ret i1 %4 +} +declare {i64, i1} @llvm.usub.with.overflow.i64(i64, i64) +define i1 @usub.with.overflow.i64(i64, i64) { + call {i64, i1} @llvm.usub.with.overflow.i64(i64 %0, i64 %1) + extractvalue {i64, i1} %3, 1 + ret i1 %4 +} + +declare i8 @llvm.sadd.sat.i8(i8, i8) +define i8 @sadd.sat.i8(i8, i8) { + call i8 @llvm.sadd.sat.i8(i8 %0, i8 %1) + ret i8 %3 +} +declare i16 @llvm.sadd.sat.i16(i16, i16) +define i16 @sadd.sat.i16(i16, i16) { + call i16 @llvm.sadd.sat.i16(i16 %0, i16 %1) + ret i16 %3 +} +declare i32 @llvm.sadd.sat.i32(i32, i32) +define i32 @sadd.sat.i32(i32, i32) { + call i32 @llvm.sadd.sat.i32(i32 %0, i32 %1) + ret i32 %3 +} +declare i64 @llvm.sadd.sat.i64(i64, i64) +define i64 @sadd.sat.i64(i64, i64) { + call i64 @llvm.sadd.sat.i64(i64 %0, i64 %1) + ret i64 %3 +} + +declare i8 @llvm.uadd.sat.i8(i8, i8) +define i8 @uadd.sat.i8(i8, i8) { + call i8 @llvm.uadd.sat.i8(i8 %0, i8 %1) + ret i8 %3 +} +declare i16 @llvm.uadd.sat.i16(i16, i16) +define i16 @uadd.sat.i16(i16, i16) { + call i16 @llvm.uadd.sat.i16(i16 %0, i16 %1) + ret i16 %3 +} +declare i32 @llvm.uadd.sat.i32(i32, i32) +define i32 @uadd.sat.i32(i32, i32) { + call i32 @llvm.uadd.sat.i32(i32 %0, i32 %1) + ret i32 %3 +} +declare i64 @llvm.uadd.sat.i64(i64, i64) +define i64 @uadd.sat.i64(i64, i64) { + call i64 @llvm.uadd.sat.i64(i64 %0, i64 %1) + ret i64 %3 +} + +declare i8 @llvm.ssub.sat.i8(i8, i8) +define i8 @ssub.sat.i8(i8, i8) { + call i8 @llvm.ssub.sat.i8(i8 %0, i8 %1) + ret i8 %3 +} +declare i16 @llvm.ssub.sat.i16(i16, i16) +define i16 @ssub.sat.i16(i16, i16) { + call i16 @llvm.ssub.sat.i16(i16 %0, i16 %1) + ret i16 %3 +} +declare i32 @llvm.ssub.sat.i32(i32, i32) +define i32 @ssub.sat.i32(i32, i32) { + call i32 @llvm.ssub.sat.i32(i32 %0, i32 %1) + ret i32 %3 +} +declare i64 @llvm.ssub.sat.i64(i64, i64) +define i64 @ssub.sat.i64(i64, i64) { + call i64 @llvm.ssub.sat.i64(i64 %0, i64 %1) + ret i64 %3 +} + +declare i8 @llvm.usub.sat.i8(i8, i8) +define i8 @usub.sat.i8(i8, i8) { + call i8 @llvm.usub.sat.i8(i8 %0, i8 %1) + ret i8 %3 +} +declare i16 @llvm.usub.sat.i16(i16, i16) +define i16 @usub.sat.i16(i16, i16) { + call i16 @llvm.usub.sat.i16(i16 %0, i16 %1) + ret i16 %3 +} +declare i32 @llvm.usub.sat.i32(i32, i32) +define i32 @usub.sat.i32(i32, i32) { + call i32 @llvm.usub.sat.i32(i32 %0, i32 %1) + ret i32 %3 +} +declare i64 @llvm.usub.sat.i64(i64, i64) +define i64 @usub.sat.i64(i64, i64) { + call i64 @llvm.usub.sat.i64(i64 %0, i64 %1) + ret i64 %3 +} + +declare i8 @llvm.smul.sat.i8(i8, i8) +define i8 @smul.sat.i8(i8, i8) { + call i8 @llvm.smul.sat.i8(i8 %0, i8 %1) + ret i8 %3 +} +declare i16 @llvm.smul.sat.i16(i16, i16) +define i16 @smul.sat.i16(i16, i16) { + call i16 @llvm.smul.sat.i16(i16 %0, i16 %1) + ret i16 %3 +} +declare i32 @llvm.smul.sat.i32(i32, i32) +define i32 @smul.sat.i32(i32, i32) { + call i32 @llvm.smul.sat.i32(i32 %0, i32 %1) + ret i32 %3 +} +declare i64 @llvm.smul.sat.i64(i64, i64) +define i64 @smul.sat.i64(i64, i64) { + call i64 @llvm.smul.sat.i64(i64 %0, i64 %1) + ret i64 %3 +} + +declare i8 @llvm.umul.sat.i8(i8, i8) +define i8 @umul.sat.i8(i8, i8) { + call i8 @llvm.umul.sat.i8(i8 %0, i8 %1) + ret i8 %3 +} +declare i16 @llvm.umul.sat.i16(i16, i16) +define i16 @umul.sat.i16(i16, i16) { + call i16 @llvm.umul.sat.i16(i16 %0, i16 %1) + ret i16 %3 +} +declare i32 @llvm.umul.sat.i32(i32, i32) +define i32 @umul.sat.i32(i32, i32) { + call i32 @llvm.umul.sat.i32(i32 %0, i32 %1) + ret i32 %3 +} +declare i64 @llvm.umul.sat.i64(i64, i64) +define i64 @umul.sat.i64(i64, i64) { + call i64 @llvm.umul.sat.i64(i64 %0, i64 %1) + ret i64 %3 +} diff --git a/llvm/test/CodeGen/Z80/intrinsics24.ll b/llvm/test/CodeGen/Z80/intrinsics24.ll new file mode 100644 index 00000000000000..22b87820c3c883 --- /dev/null +++ b/llvm/test/CodeGen/Z80/intrinsics24.ll @@ -0,0 +1,101 @@ +; RUN: llc -mtriple=ez80 < %s + +declare i24 @llvm.bitreverse.i24(i24) +define i24 @bitreverse.i24(i24) { + call i24 @llvm.bitreverse.i24(i24 %0) + ret i24 %2 +} + +declare i24 @llvm.ctpop.i24(i24) +define i24 @ctpop.i24(i24) { + call i24 @llvm.ctpop.i24(i24 %0) + ret i24 %2 +} + +declare i24 @llvm.ctlz.i24(i24) +define i24 @ctlz.i24(i24) { + call i24 @llvm.ctlz.i24(i24 %0) + ret i24 %2 +} + +declare i24 @llvm.cttz.i24(i24) +define i24 @cttz.i24(i24) { + call i24 @llvm.cttz.i24(i24 %0) + ret i24 %2 +} + +declare i24 @llvm.fshl.i24(i24, i24, i24) +define i24 @fshl.i24(i24, i24, i24) { + call i24 @llvm.fshl.i24(i24 %0, i24 %1, i24 %2) + ret i24 %4 +} + +declare i24 @llvm.fshr.i24(i24, i24, i24) +define i24 @fshr.i24(i24, i24, i24) { + call i24 @llvm.fshr.i24(i24 %0, i24 %1, i24 %2) + ret i24 %4 +} + +declare {i24, i1} @llvm.sadd.with.overflow.i24(i24, i24) +define i1 @sadd.with.overflow.i24(i24, i24) { + call {i24, i1} @llvm.sadd.with.overflow.i24(i24 %0, i24 %1) + extractvalue {i24, i1} %3, 1 + ret i1 %4 +} + +declare {i24, i1} @llvm.uadd.with.overflow.i24(i24, i24) +define i1 @uadd.with.overflow.i24(i24, i24) { + call {i24, i1} @llvm.uadd.with.overflow.i24(i24 %0, i24 %1) + extractvalue {i24, i1} %3, 1 + ret i1 %4 +} + +declare {i24, i1} @llvm.ssub.with.overflow.i24(i24, i24) +define i1 @ssub.with.overflow.i24(i24, i24) { + call {i24, i1} @llvm.ssub.with.overflow.i24(i24 %0, i24 %1) + extractvalue {i24, i1} %3, 1 + ret i1 %4 +} + +declare {i24, i1} @llvm.usub.with.overflow.i24(i24, i24) +define i1 @usub.with.overflow.i24(i24, i24) { + call {i24, i1} @llvm.usub.with.overflow.i24(i24 %0, i24 %1) + extractvalue {i24, i1} %3, 1 + ret i1 %4 +} + +declare i24 @llvm.sadd.sat.i24(i24, i24) +define i24 @sadd.sat.i24(i24, i24) { + call i24 @llvm.sadd.sat.i24(i24 %0, i24 %1) + ret i24 %3 +} + +declare i24 @llvm.uadd.sat.i24(i24, i24) +define i24 @uadd.sat.i24(i24, i24) { + call i24 @llvm.uadd.sat.i24(i24 %0, i24 %1) + ret i24 %3 +} + +declare i24 @llvm.ssub.sat.i24(i24, i24) +define i24 @ssub.sat.i24(i24, i24) { + call i24 @llvm.ssub.sat.i24(i24 %0, i24 %1) + ret i24 %3 +} + +declare i24 @llvm.usub.sat.i24(i24, i24) +define i24 @usub.sat.i24(i24, i24) { + call i24 @llvm.usub.sat.i24(i24 %0, i24 %1) + ret i24 %3 +} + +declare i24 @llvm.smul.sat.i24(i24, i24) +define i24 @smul.sat.i24(i24, i24) { + call i24 @llvm.smul.sat.i24(i24 %0, i24 %1) + ret i24 %3 +} + +declare i24 @llvm.umul.sat.i24(i24, i24) +define i24 @umul.sat.i24(i24, i24) { + call i24 @llvm.umul.sat.i24(i24 %0, i24 %1) + ret i24 %3 +} diff --git a/llvm/test/CodeGen/Z80/ops.ll b/llvm/test/CodeGen/Z80/ops.ll new file mode 100644 index 00000000000000..8ee9c390d57fb2 --- /dev/null +++ b/llvm/test/CodeGen/Z80/ops.ll @@ -0,0 +1,778 @@ +; RUN: llc -mtriple=z80 < %s +; RUN: llc -mtriple=ez80-code16 < %s +; RUN: llc -mtriple=ez80 < %s + +define void @ret.void() { + ret void +} +define i8 @ret.i8(i8) { + ret i8 %0 +} +define i16 @ret.i16(i16) { + ret i16 %0 +} +define i24 @ret.i24(i24) { + ret i24 %0 +} +define i32 @ret.i32(i32) { + ret i32 %0 +} +define i48 @ret.i48(i48) { + ret i48 %0 +} +define i64 @ret.i64(i64) { + ret i64 %0 +} +define float @ret.float(float) { + ret float %0 +} +define double @ret.double(double) { + ret double %0 +} + +define void @br() { + br label %1 + ret void +} +define i8 @br.i1(i1) { + br i1 %0, label %2, label %3 + ret i8 1 + ret i8 0 +} + +define i8 @switch(i8) { + switch i8 %0, label %2 [ i8 0, label %3 + i8 1, label %4 + i8 2, label %5 + i8 3, label %6 ] + ret i8 -1 + ret i8 0 + ret i8 1 + ret i8 2 + ret i8 3 +} + +define i8 @indirectbr(i8*) { + indirectbr i8* %0, [label %2, label %3] + ret i8 1 + ret i8 0 +} + +define i8 @shl.i8(i8, i8) { + shl i8 %0, %1 + ret i8 %3 +} +define i16 @shl.i16(i16, i16) { + shl i16 %0, %1 + ret i16 %3 +} +define i32 @shl.i32(i32, i32) { + shl i32 %0, %1 + ret i32 %3 +} +define i64 @shl.i64(i64, i64) { + shl i64 %0, %1 + ret i64 %3 +} + +define i8 @lshr.i8(i8, i8) { + lshr i8 %0, %1 + ret i8 %3 +} +define i16 @lshr.i16(i16, i16) { + lshr i16 %0, %1 + ret i16 %3 +} +define i32 @lshr.i32(i32, i32) { + lshr i32 %0, %1 + ret i32 %3 +} +define i64 @lshr.i64(i64, i64) { + lshr i64 %0, %1 + ret i64 %3 +} + +define i8 @ashr.i8(i8, i8) { + ashr i8 %0, %1 + ret i8 %3 +} +define i16 @ashr.i16(i16, i16) { + ashr i16 %0, %1 + ret i16 %3 +} +define i32 @ashr.i32(i32, i32) { + ashr i32 %0, %1 + ret i32 %3 +} +define i64 @ashr.i64(i64, i64) { + ashr i64 %0, %1 + ret i64 %3 +} + +define i8 @and.i8(i8, i8) { + and i8 %0, %1 + ret i8 %3 +} +define i16 @and.i16(i16, i16) { + and i16 %0, %1 + ret i16 %3 +} +define i32 @and.i32(i32, i32) { + and i32 %0, %1 + ret i32 %3 +} +define i64 @and.i64(i64, i64) { + and i64 %0, %1 + ret i64 %3 +} + +define i8 @or.i8(i8, i8) { + or i8 %0, %1 + ret i8 %3 +} +define i16 @or.i16(i16, i16) { + or i16 %0, %1 + ret i16 %3 +} +define i32 @or.i32(i32, i32) { + or i32 %0, %1 + ret i32 %3 +} +define i64 @or.i64(i64, i64) { + or i64 %0, %1 + ret i64 %3 +} + +define i8 @xor.i8(i8, i8) { + xor i8 %0, %1 + ret i8 %3 +} +define i16 @xor.i16(i16, i16) { + xor i16 %0, %1 + ret i16 %3 +} +define i32 @xor.i32(i32, i32) { + xor i32 %0, %1 + ret i32 %3 +} +define i64 @xor.i64(i64, i64) { + xor i64 %0, %1 + ret i64 %3 +} + +define i8 @add.i8(i8, i8) { + add i8 %0, %1 + ret i8 %3 +} +define i16 @add.i16(i16, i16) { + add i16 %0, %1 + ret i16 %3 +} +define i24 @add.i24(i24, i24) { + add i24 %0, %1 + ret i24 %3 +} +define i32 @add.i32(i32, i32) { + add i32 %0, %1 + ret i32 %3 +} +define i48 @add.i48(i48, i48) { + add i48 %0, %1 + ret i48 %3 +} +define i64 @add.i64(i64, i64) { + add i64 %0, %1 + ret i64 %3 +} + +define i8 @sub.i8(i8, i8) { + sub i8 %0, %1 + ret i8 %3 +} +define i16 @sub.i16(i16, i16) { + sub i16 %0, %1 + ret i16 %3 +} +define i24 @sub.i24(i24, i24) { + sub i24 %0, %1 + ret i24 %3 +} +define i32 @sub.i32(i32, i32) { + sub i32 %0, %1 + ret i32 %3 +} +define i48 @sub.i48(i48, i48) { + sub i48 %0, %1 + ret i48 %3 +} +define i64 @sub.i64(i64, i64) { + sub i64 %0, %1 + ret i64 %3 +} + +define i8 @mul.i8(i8, i8) { + mul i8 %0, %1 + ret i8 %3 +} +define i16 @mul.i16(i16, i16) { + mul i16 %0, %1 + ret i16 %3 +} +define i32 @mul.i32(i32, i32) { + mul i32 %0, %1 + ret i32 %3 +} +define i64 @mul.i64(i64, i64) { + mul i64 %0, %1 + ret i64 %3 +} + +define i8 @udiv.i8(i8, i8) { + udiv i8 %0, %1 + ret i8 %3 +} +define i16 @udiv.i16(i16, i16) { + udiv i16 %0, %1 + ret i16 %3 +} +define i32 @udiv.i32(i32, i32) { + udiv i32 %0, %1 + ret i32 %3 +} +define i64 @udiv.i64(i64, i64) { + udiv i64 %0, %1 + ret i64 %3 +} + +define i8 @sdiv.i8(i8, i8) { + sdiv i8 %0, %1 + ret i8 %3 +} +define i16 @sdiv.i16(i16, i16) { + sdiv i16 %0, %1 + ret i16 %3 +} +define i32 @sdiv.i32(i32, i32) { + sdiv i32 %0, %1 + ret i32 %3 +} +define i64 @sdiv.i64(i64, i64) { + sdiv i64 %0, %1 + ret i64 %3 +} + +define i8 @urem.i8(i8, i8) { + urem i8 %0, %1 + ret i8 %3 +} +define i16 @urem.i16(i16, i16) { + urem i16 %0, %1 + ret i16 %3 +} +define i32 @urem.i32(i32, i32) { + urem i32 %0, %1 + ret i32 %3 +} +define i64 @urem.i64(i64, i64) { + urem i64 %0, %1 + ret i64 %3 +} + +define i8 @srem.i8(i8, i8) { + srem i8 %0, %1 + ret i8 %3 +} +define i16 @srem.i16(i16, i16) { + srem i16 %0, %1 + ret i16 %3 +} +define i32 @srem.i32(i32, i32) { + srem i32 %0, %1 + ret i32 %3 +} +define i64 @srem.i64(i64, i64) { + srem i64 %0, %1 + ret i64 %3 +} + +define float @fneg.float(float) { + fneg float %0 + ret float %2 +} +define double @fneg.double(double) { + fneg double %0 + ret double %2 +} + +define float @fadd.float(float, float) { + fadd float %0, %1 + ret float %3 +} +define double @fadd.double(double, double) { + fadd double %0, %1 + ret double %3 +} + +define float @fsub.float(float, float) { + fsub float %0, %1 + ret float %3 +} +define double @fsub.double(double, double) { + fsub double %0, %1 + ret double %3 +} + +define float @fmul.float(float, float) { + fmul float %0, %1 + ret float %3 +} +define double @fmul.double(double, double) { + fmul double %0, %1 + ret double %3 +} + +define float @fdiv.float(float, float) { + fdiv float %0, %1 + ret float %3 +} +define double @fdiv.double(double, double) { + fdiv double %0, %1 + ret double %3 +} + +define float @frem.float(float, float) { + frem float %0, %1 + ret float %3 +} +define double @frem.double(double, double) { + frem double %0, %1 + ret double %3 +} + +define i8 @trunc.i16.i8(i16) { + trunc i16 %0 to i8 + ret i8 %2 +} +define i8 @trunc.i24.i8(i24) { + trunc i24 %0 to i8 + ret i8 %2 +} +define i8 @trunc.i32.i8(i32) { + trunc i32 %0 to i8 + ret i8 %2 +} +define i8 @trunc.i48.i8(i48) { + trunc i48 %0 to i8 + ret i8 %2 +} +define i8 @trunc.i64.i8(i64) { + trunc i64 %0 to i8 + ret i8 %2 +} +define i16 @trunc.i24.i16(i24) { + trunc i24 %0 to i16 + ret i16 %2 +} +define i16 @trunc.i32.i16(i32) { + trunc i32 %0 to i16 + ret i16 %2 +} +define i16 @trunc.i48.i16(i48) { + trunc i48 %0 to i16 + ret i16 %2 +} +define i16 @trunc.i64.i16(i64) { + trunc i64 %0 to i16 + ret i16 %2 +} +define i24 @trunc.i32.i24(i32) { + trunc i32 %0 to i24 + ret i24 %2 +} +define i24 @trunc.i48.i24(i48) { + trunc i48 %0 to i24 + ret i24 %2 +} +define i24 @trunc.i64.i24(i64) { + trunc i64 %0 to i24 + ret i24 %2 +} +define i32 @trunc.i48.i32(i48) { + trunc i48 %0 to i32 + ret i32 %2 +} +define i32 @trunc.i64.i32(i64) { + trunc i64 %0 to i32 + ret i32 %2 +} +define i48 @trunc.i64.i48(i64) { + trunc i64 %0 to i48 + ret i48 %2 +} + +define i16 @zext.i8.i16(i8) { + zext i8 %0 to i16 + ret i16 %2 +} +define i24 @zext.i8.i24(i8) { + zext i8 %0 to i24 + ret i24 %2 +} +define i32 @zext.i8.i32(i8) { + zext i8 %0 to i32 + ret i32 %2 +} +define i48 @zext.i8.i48(i8) { + zext i8 %0 to i48 + ret i48 %2 +} +define i64 @zext.i8.i64(i8) { + zext i8 %0 to i64 + ret i64 %2 +} +define i24 @zext.i16.i24(i16) { + zext i16 %0 to i24 + ret i24 %2 +} +define i32 @zext.i16.i32(i16) { + zext i16 %0 to i32 + ret i32 %2 +} +define i48 @zext.i16.i48(i16) { + zext i16 %0 to i48 + ret i48 %2 +} +define i64 @zext.i16.i64(i16) { + zext i16 %0 to i64 + ret i64 %2 +} +define i32 @zext.i24.i32(i24) { + zext i24 %0 to i32 + ret i32 %2 +} +define i48 @zext.i24.i48(i24) { + zext i24 %0 to i48 + ret i48 %2 +} +define i64 @zext.i24.i64(i24) { + zext i24 %0 to i64 + ret i64 %2 +} +define i48 @zext.i32.i48(i32) { + zext i32 %0 to i48 + ret i48 %2 +} +define i64 @zext.i32.i64(i32) { + zext i32 %0 to i64 + ret i64 %2 +} +define i64 @zext.i48.i64(i48) { + zext i48 %0 to i64 + ret i64 %2 +} + +define i16 @sext.i8.i16(i8) { + sext i8 %0 to i16 + ret i16 %2 +} +define i24 @sext.i8.i24(i8) { + sext i8 %0 to i24 + ret i24 %2 +} +define i32 @sext.i8.i32(i8) { + sext i8 %0 to i32 + ret i32 %2 +} +define i48 @sext.i8.i48(i8) { + sext i8 %0 to i48 + ret i48 %2 +} +define i64 @sext.i8.i64(i8) { + sext i8 %0 to i64 + ret i64 %2 +} +define i24 @sext.i16.i24(i16) { + sext i16 %0 to i24 + ret i24 %2 +} +define i32 @sext.i16.i32(i16) { + sext i16 %0 to i32 + ret i32 %2 +} +define i48 @sext.i16.i48(i16) { + sext i16 %0 to i48 + ret i48 %2 +} +define i64 @sext.i16.i64(i16) { + sext i16 %0 to i64 + ret i64 %2 +} +define i32 @sext.i24.i32(i24) { + sext i24 %0 to i32 + ret i32 %2 +} +define i48 @sext.i24.i48(i24) { + sext i24 %0 to i48 + ret i48 %2 +} +define i64 @sext.i24.i64(i24) { + sext i24 %0 to i64 + ret i64 %2 +} +define i48 @sext.i32.i48(i32) { + sext i32 %0 to i48 + ret i48 %2 +} +define i64 @sext.i32.i64(i32) { + sext i32 %0 to i64 + ret i64 %2 +} +define i64 @sext.i48.i64(i48) { + sext i48 %0 to i64 + ret i64 %2 +} + +define float @fptrunc.double.float(double) { + fptrunc double %0 to float + ret float %2 +} + +define double @fpext.float.double(float) { + fpext float %0 to double + ret double %2 +} + +define i8 @fptoui.float.i8(float) { + fptoui float %0 to i8 + ret i8 %2 +} +define i16 @fptoui.float.i16(float) { + fptoui float %0 to i16 + ret i16 %2 +} +define i24 @fptoui.float.i24(float) { + fptoui float %0 to i24 + ret i24 %2 +} +define i32 @fptoui.float.i32(float) { + fptoui float %0 to i32 + ret i32 %2 +} +define i64 @fptoui.float.i64(float) { + fptoui float %0 to i64 + ret i64 %2 +} +define i8 @fptoui.double.i8(double) { + fptoui double %0 to i8 + ret i8 %2 +} +define i16 @fptoui.double.i16(double) { + fptoui double %0 to i16 + ret i16 %2 +} +define i24 @fptoui.double.i24(double) { + fptoui double %0 to i24 + ret i24 %2 +} +define i32 @fptoui.double.i32(double) { + fptoui double %0 to i32 + ret i32 %2 +} +define i64 @fptoui.double.i64(double) { + fptoui double %0 to i64 + ret i64 %2 +} + +define i8 @fptosi.float.i8(float) { + fptosi float %0 to i8 + ret i8 %2 +} +define i16 @fptosi.float.i16(float) { + fptosi float %0 to i16 + ret i16 %2 +} +define i24 @fptosi.float.i24(float) { + fptosi float %0 to i24 + ret i24 %2 +} +define i32 @fptosi.float.i32(float) { + fptosi float %0 to i32 + ret i32 %2 +} +define i64 @fptosi.float.i64(float) { + fptosi float %0 to i64 + ret i64 %2 +} +define i8 @fptosi.double.i8(double) { + fptosi double %0 to i8 + ret i8 %2 +} +define i16 @fptosi.double.i16(double) { + fptosi double %0 to i16 + ret i16 %2 +} +define i24 @fptosi.double.i24(double) { + fptosi double %0 to i24 + ret i24 %2 +} +define i32 @fptosi.double.i32(double) { + fptosi double %0 to i32 + ret i32 %2 +} +define i64 @fptosi.double.i64(double) { + fptosi double %0 to i64 + ret i64 %2 +} + +define float @uitofp.i8.float(i8) { + uitofp i8 %0 to float + ret float %2 +} +define float @uitofp.i16.float(i16) { + uitofp i16 %0 to float + ret float %2 +} +define float @uitofp.i24.float(i24) { + uitofp i24 %0 to float + ret float %2 +} +define float @uitofp.i32.float(i32) { + uitofp i32 %0 to float + ret float %2 +} +define float @uitofp.i64.float(i64) { + uitofp i64 %0 to float + ret float %2 +} +define double @uitofp.i8.double(i8) { + uitofp i8 %0 to double + ret double %2 +} +define double @uitofp.i16.double(i16) { + uitofp i16 %0 to double + ret double %2 +} +define double @uitofp.i24.double(i24) { + uitofp i24 %0 to double + ret double %2 +} +define double @uitofp.i32.double(i32) { + uitofp i32 %0 to double + ret double %2 +} +define double @uitofp.i64.double(i64) { + uitofp i64 %0 to double + ret double %2 +} + +define float @sitofp.i8.float(i8) { + sitofp i8 %0 to float + ret float %2 +} +define float @sitofp.i16.float(i16) { + sitofp i16 %0 to float + ret float %2 +} +define float @sitofp.i24.float(i24) { + sitofp i24 %0 to float + ret float %2 +} +define float @sitofp.i32.float(i32) { + sitofp i32 %0 to float + ret float %2 +} +define float @sitofp.i64.float(i64) { + sitofp i64 %0 to float + ret float %2 +} +define double @sitofp.i8.double(i8) { + sitofp i8 %0 to double + ret double %2 +} +define double @sitofp.i16.double(i16) { + sitofp i16 %0 to double + ret double %2 +} +define double @sitofp.i24.double(i24) { + sitofp i24 %0 to double + ret double %2 +} +define double @sitofp.i32.double(i32) { + sitofp i32 %0 to double + ret double %2 +} +define double @sitofp.i64.double(i64) { + sitofp i64 %0 to double + ret double %2 +} + +define i8 @ptrtoint.i8(i8*) { + ptrtoint i8* %0 to i8 + ret i8 %2 +} +define i16 @ptrtoint.i16(i16*) { + ptrtoint i16* %0 to i16 + ret i16 %2 +} +define i24 @ptrtoint.i24(i24*) { + ptrtoint i24* %0 to i24 + ret i24 %2 +} +define i32 @ptrtoint.i32(i32*) { + ptrtoint i32* %0 to i32 + ret i32 %2 +} +define i48 @ptrtoint.i48(i48*) { + ptrtoint i48* %0 to i48 + ret i48 %2 +} +define i64 @ptrtoint.i64(i64*) { + ptrtoint i64* %0 to i64 + ret i64 %2 +} + +define i8* @inttoptr.i8(i8) { + inttoptr i8 %0 to i8* + ret i8* %2 +} +define i16* @inttoptr.i16(i16) { + inttoptr i16 %0 to i16* + ret i16* %2 +} +define i24* @inttoptr.i24(i24) { + inttoptr i24 %0 to i24* + ret i24* %2 +} +define i32* @inttoptr.i32(i32) { + inttoptr i32 %0 to i32* + ret i32* %2 +} +define i48* @inttoptr.i48(i48) { + inttoptr i48 %0 to i48* + ret i48* %2 +} +define i64* @inttoptr.i64(i64) { + inttoptr i64 %0 to i64* + ret i64* %2 +} + +define i32 @bitcast.float.i32(float) { + bitcast float %0 to i32 + ret i32 %2 +} +define i64 @bitcast.double.i64(double) { + bitcast double %0 to i64 + ret i64 %2 +} +define float @bitcast.i32.float(i32) { + bitcast i32 %0 to float + ret float %2 +} +define double @bitcast.i64.double(i64) { + bitcast i64 %0 to double + ret double %2 +} +define i16* @bitcast.p0i8.p0i16(i8*) { + bitcast i8* %0 to i16* + ret i16* %2 +} diff --git a/llvm/test/CodeGen/Z80/ops24.ll b/llvm/test/CodeGen/Z80/ops24.ll new file mode 100644 index 00000000000000..02238f954c1961 --- /dev/null +++ b/llvm/test/CodeGen/Z80/ops24.ll @@ -0,0 +1,46 @@ +; RUN: llc -mtriple=ez80 < %s + +define i24 @shl.i24(i24, i24) { + shl i24 %0, %1 + ret i24 %3 +} +define i24 @lshr.i24(i24, i24) { + lshr i24 %0, %1 + ret i24 %3 +} +define i24 @ashr.i24(i24, i24) { + ashr i24 %0, %1 + ret i24 %3 +} +define i24 @and.i24(i24, i24) { + and i24 %0, %1 + ret i24 %3 +} +define i24 @or.i24(i24, i24) { + or i24 %0, %1 + ret i24 %3 +} +define i24 @xor.i24(i24, i24) { + xor i24 %0, %1 + ret i24 %3 +} +define i24 @mul.i24(i24, i24) { + mul i24 %0, %1 + ret i24 %3 +} +define i24 @udiv.i24(i24, i24) { + udiv i24 %0, %1 + ret i24 %3 +} +define i24 @sdiv.i24(i24, i24) { + sdiv i24 %0, %1 + ret i24 %3 +} +define i24 @urem.i24(i24, i24) { + urem i24 %0, %1 + ret i24 %3 +} +define i24 @srem.i24(i24, i24) { + srem i24 %0, %1 + ret i24 %3 +} diff --git a/llvm/utils/UpdateTestChecks/asm.py b/llvm/utils/UpdateTestChecks/asm.py index 998d8b1f57736a..426114505c2271 100644 --- a/llvm/utils/UpdateTestChecks/asm.py +++ b/llvm/utils/UpdateTestChecks/asm.py @@ -140,6 +140,16 @@ class string: r'^\s*(\.Lfunc_end[0-9]+:\n|end_function)', flags=(re.M | re.S)) +ASM_FUNCTION_Z80_RE = re.compile( + r'^_(?P[^:]+):\s*;+\s*@(?P=func)\n' + r'(?P.*?)\n' + r'^\s*;\s--\sEnd\sfunction', + flags=(re.M | re.S)) + + +SCRUB_LOOP_COMMENT_RE = re.compile( + r'# =>This Inner Loop Header:.*|# in Loop:.*', flags=re.M) + SCRUB_X86_SHUFFLES_RE = ( re.compile( r'^(\s*\w+) [^#\n]+#+ ((?:[xyz]mm\d+|mem)( \{%k\d+\}( \{z\})?)? = .*)$', @@ -307,6 +317,16 @@ def scrub_asm_wasm32(asm, args): asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) return asm +def scrub_asm_z80(asm, args): + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) + return asm + def get_triple_from_march(march): triples = { 'amdgcn': 'amdgcn', @@ -350,6 +370,8 @@ def build_function_body_dictionary_for_triple(args, raw_tool_output, triple, pre 'sparc': (scrub_asm_sparc, ASM_FUNCTION_SPARC_RE), 's390x': (scrub_asm_systemz, ASM_FUNCTION_SYSTEMZ_RE), 'wasm32': (scrub_asm_wasm32, ASM_FUNCTION_WASM32_RE), + 'z80': (scrub_asm_z80, ASM_FUNCTION_Z80_RE), + 'ez80': (scrub_asm_z80, ASM_FUNCTION_Z80_RE), } handler = None best_prefix = '' From dcd55f97074a04d604aeca85b213cb9d1467244f Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 14 Nov 2019 02:19:38 +0100 Subject: [PATCH 18/21] Use Github Actions. --- .github/workflows/cmake.yml | 311 ++++++++++++++++++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 .github/workflows/cmake.yml diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml new file mode 100644 index 00000000000000..6c4a7142996bbe --- /dev/null +++ b/.github/workflows/cmake.yml @@ -0,0 +1,311 @@ +name: CMake + +on: + push: + branches: [z80] + +jobs: + build-llvm: + needs: [] + strategy: + fail-fast: false + matrix: + runs-on: [ubuntu-latest, macos-latest, windows-latest] + build-type: [Debug, Release] + if: always() + runs-on: ${{matrix.runs-on}} + steps: + - name: Uninstall Unused Packages + if: runner.os == 'Linux' + run: sudo apt-get remove ^ghc-[0-9.]+$ + + - name: Prepare Build Environment + if: runner.os != 'Windows' + run: | + cmake -E echo ::set-env name=CC::clang + cmake -E echo ::set-env name=CFLAGS::-std=c17 + cmake -E echo ::set-env name=CXX::clang++ + cmake -E echo ::set-env name=CXXFLAGS::-std=c++17 + - name: Prepare Build Environment + if: runner.os == 'Windows' + run: | + git config --global core.autocrlf false + $path = vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath + Write-Output "::add-path::$(join-path $path 'VC\Tools\Llvm\bin')" + cmd /s /c """$(Join-Path $path 'Common7\Tools\vsdevcmd.bat')"" && set" | where { $_ -match '(\w+)=(.*)' } | foreach { Write-Output "::set-env name=$($Matches[1])::$($Matches[2])" } + cmake -E echo ::set-env name=CC::clang-cl + cmake -E echo ::set-env name=CXX::clang-cl + cmake -E echo ::set-env name=EXE::.exe + cmake -E echo ::set-env name=PY::.py + + - name: Prepare Build Environment + if: matrix.build-type == 'Debug' + run: cmake -E echo ::set-env name=DYLIB::ON + - name: Prepare Build Environment + if: matrix.build-type != 'Debug' + run: cmake -E echo ::set-env name=DYLIB::OFF + + - name: Install Build Dependencies + if: runner.os == 'Linux' + run: | + sudo apt-get install -y ninja-build lld + cmake -E echo ::set-env name=LD::lld + - name: Install Build Dependencies + if: runner.os == 'macOS' + run: | + brew install ninja + cmake -E echo ::add-path::/usr/local/opt/llvm/bin + - name: Install Build Dependencies + if: runner.os == 'Windows' + run: pip install ninja + + - name: Checkout Project + uses: actions/checkout@v2 + with: + path: src + - name: Sparse Checkout + working-directory: src + run: | + git version + git sparse-checkout init --cone + git sparse-checkout set llvm clang + + - name: Configure LLVM + run: cmake -Ssrc/llvm -Bbuild -GNinja -DCMAKE_BUILD_TYPE:STRING=${{matrix.build-type}} -DCMAKE_INSTALL_PREFIX:PATH="${{github.workspace}}/llvm" -DCMAKE_C_COMPILER:FILEPATH="${{env.CC}}" -DCMAKE_C_FLAGS:STRING="${{env.CFLAGS}}" -DCMAKE_CXX_COMPILER:FILEPATH="${{env.CXX}}" -DCMAKE_CXX_FLAGS:STRING="${{env.CXXFLAGS}}" -DLLVM_PARALLEL_LINK_JOBS:STRING=1 -DLLVM_USE_LINKER="${{env.LD}}" -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD:STRING="Z80" -DLLVM_LINK_LLVM_DYLIB:BOOL=${{env.DYLIB}} -DLLVM_INCLUDE_TESTS:BOOL=OFF -DLLVM_TOOL_DSYMUTIL_BUILD:BOOL=OFF -DLLVM_TOOL_GOLD_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_AS_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_CFI_VERIFY_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_CVTRES_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_DWP_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_ELFABI_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_EXEGESIS_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_GSYMUTIL_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_IFS_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_ISEL_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_ITANIUM_DEMANGLE_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_JITLINK_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_JITLISTENER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_LIPO_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_LTO2_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_LTO_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_MCA_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_MC_ASSEMBLE_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_MC_DISASSEMBLE_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_MICROSOFT_DEMANGLE_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_MT_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_OPT_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_PDBUTIL_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_PROFDATA_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_RC_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_SPECIAL_CASE_LIST_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_UNDNAME_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_XRAY_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_YAML_NUMERIC_PARSER_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LTO_BUILD:BOOL=OFF -DLLVM_TOOL_SANCOV_BUILD:BOOL=OFF -DLLVM_TOOL_SANSTATS_BUILD:BOOL=OFF -DLLVM_TOOL_VFABI_DEMANGLE_FUZZER_BUILD:BOOL=OFF + - name: Build LLVM + run: cmake --build build --config ${{matrix.build-type}} + + - name: Install LLVM + run: cmake --build build --config ${{matrix.build-type}} --target install + - name: Upload LLVM + uses: actions/upload-artifact@v1 + with: + name: LLVM${{runner.os}}${{matrix.build-type}} + path: llvm + + - name: Configure Test + run: cmake build -DLLVM_LINK_LLVM_DYLIB:BOOL=${{env.DYLIB}} -DLLVM_ENABLE_PROJECTS:STRING=clang -DLLVM_INCLUDE_TESTS:BOOL=ON -DLLVM_TOOL_DSYMUTIL_BUILD:BOOL=ON -DLLVM_TOOL_GOLD_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_AS_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_CFI_VERIFY_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_CVTRES_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_DWP_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_ELFABI_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_EXEGESIS_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_GSYMUTIL_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_IFS_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_ISEL_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_ITANIUM_DEMANGLE_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_JITLINK_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_JITLISTENER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_LIPO_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_LTO2_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_LTO_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_MCA_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_MC_ASSEMBLE_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_MC_DISASSEMBLE_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_MICROSOFT_DEMANGLE_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_MT_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_OPT_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_PDBUTIL_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_PROFDATA_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_RC_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_SPECIAL_CASE_LIST_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_UNDNAME_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_XRAY_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_YAML_NUMERIC_PARSER_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LTO_BUILD:BOOL=ON -DLLVM_TOOL_SANCOV_BUILD:BOOL=ON -DLLVM_TOOL_SANSTATS_BUILD:BOOL=ON -DLLVM_TOOL_VFABI_DEMANGLE_FUZZER_BUILD:BOOL=ON + - name: Install Test + run: | + cmake -E make_directory test/bin test/test/Unit test/tools/clang/bin test/tools/clang/test/Unit + cmake -E copy build/bin/not${{env.EXE}} build/bin/FileCheck${{env.EXE}} build/bin/llvm-PerfectShuffle${{env.EXE}} build/bin/lli-child-target${{env.EXE}} build/bin/llvm-lit${{env.PY}} build/bin/llvm-locstats build/bin/count${{env.EXE}} build/bin/yaml-bench${{env.EXE}} test/bin + cmake -E copy build/test/lit.site.cfg.py test/test + cmake -E copy build/test/Unit/lit.site.cfg.py test/test/Unit + cmake -E copy build/tools/clang/bin/gen_ast_dump_json_test.py test/tools/clang/bin + cmake -E copy build/tools/clang/test/lit.site.cfg.py test/tools/clang/test + cmake -E copy build/tools/clang/test/Unit/lit.site.cfg.py test/tools/clang/test/Unit + - name: Upload Test + uses: actions/upload-artifact@v1 + with: + name: Test${{runner.os}}${{matrix.build-type}} + path: test + + - name: Disk Usage + if: always() && runner.os != 'Windows' + run: df -h + - name: Disk Usage + if: always() && runner.os == 'Windows' + run: wmic logicaldisk get size,freespace,caption + + build-clang: + needs: [build-llvm] + strategy: + fail-fast: false + matrix: + runs-on: [ubuntu-latest, macos-latest, windows-latest] + build-type: [Debug, Release] + if: always() + runs-on: ${{matrix.runs-on}} + steps: + - name: Uninstall Unused Packages + if: runner.os == 'Linux' + run: sudo apt-get remove ^ghc-[0-9.]+$ + + - name: Prepare Build Environment + if: runner.os != 'Windows' + run: | + cmake -E echo ::set-env name=CC::clang + cmake -E echo ::set-env name=CFLAGS::-std=c17 + cmake -E echo ::set-env name=CXX::clang++ + cmake -E echo ::set-env name=CXXFLAGS::-std=c++17 + - name: Prepare Build Environment + if: runner.os == 'Windows' + run: | + git config --global core.autocrlf false + $path = vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath + Write-Output "::add-path::$(join-path $path 'VC\Tools\Llvm\bin')" + cmd /s /c """$(Join-Path $path 'Common7\Tools\vsdevcmd.bat')"" && set" | where { $_ -match '(\w+)=(.*)' } | foreach { Write-Output "::set-env name=$($Matches[1])::$($Matches[2])" } + cmake -E echo ::set-env name=CC::clang-cl + cmake -E echo ::set-env name=CXX::clang-cl + cmake -E echo ::set-env name=EXE::.exe + + - name: Prepare Build Environment + if: matrix.build-type == 'Debug' + run: cmake -E echo ::set-env name=DYLIB::ON + - name: Prepare Build Environment + if: matrix.build-type != 'Debug' + run: cmake -E echo ::set-env name=DYLIB::OFF + + - name: Install Build Dependencies + if: runner.os == 'Linux' + run: | + sudo apt-get install -y ninja-build lld + cmake -E echo ::set-env name=LD::lld + - name: Install Build Dependencies + if: runner.os == 'macOS' + run: | + brew install ninja + cmake -E echo ::add-path::/usr/local/opt/llvm/bin + - name: Install Build Dependencies + if: runner.os == 'Windows' + run: pip install ninja + + - name: Checkout Project + uses: actions/checkout@v2 + with: + path: src + - name: Sparse Checkout + working-directory: src + run: | + git version + git sparse-checkout init --cone + git sparse-checkout set clang + + - name: Download LLVM + uses: actions/download-artifact@v1 + with: + name: LLVM${{runner.os}}${{matrix.build-type}} + path: llvm + - name: Set Executable + if: runner.os != 'Windows' + run: chmod +x llvm/bin/* + + - name: Configure Clang + run: cmake -Ssrc/clang -Bbuild -GNinja -DCMAKE_BUILD_TYPE:STRING=${{matrix.build-type}} -DCMAKE_INSTALL_PREFIX:PATH="${{github.workspace}}/clang" -DCMAKE_C_COMPILER:FILEPATH="${{env.CC}}" -DCMAKE_C_FLAGS:STRING="${{env.CFLAGS}}" -DCMAKE_CXX_COMPILER:FILEPATH="${{env.CXX}}" -DCMAKE_CXX_FLAGS:STRING="${{env.CXXFLAGS}}" -DLLVM_USE_LINKER="${{env.LD}}" -DLLVM_DIR:PATH="${{github.workspace}}/llvm/lib/cmake/llvm" -DCLANG_LINK_CLANG_DYLIB:BOOL=${{env.DYLIB}} + - name: Build Clang + run: cmake --build build --config ${{matrix.build-type}} + + - name: Install Clang + run: cmake --build build --config ${{matrix.build-type}} --target install + - name: Upload Clang + uses: actions/upload-artifact@v1 + with: + name: Clang${{runner.os}}${{matrix.build-type}} + path: clang + + - name: Install Executable + run: cmake -E copy clang/bin/clang${{env.EXE}} ez80-clang${{env.EXE}} + - name: Upload Executable + uses: actions/upload-artifact@v1 + with: + name: ez80-clang${{env.EXE}}-${{runner.os}}${{matrix.build-type}} + path: ez80-clang${{env.EXE}} + + - name: Download Test + uses: actions/download-artifact@v1 + with: + name: Test${{runner.os}}${{matrix.build-type}} + path: test + - name: Install Test + run: cmake -E copy build/bin/arcmt-test${{env.EXE}} build/bin/c-arcmt-test${{env.EXE}} build/bin/clang-diff${{env.EXE}} build/bin/clang-tblgen${{env.EXE}} test/bin + - name: Upload Test + uses: actions/upload-artifact@v1 + with: + name: Test${{runner.os}}${{matrix.build-type}} + path: test + + - name: Disk Usage + if: always() && runner.os != 'Windows' + run: df -h + - name: Disk Usage + if: always() && runner.os == 'Windows' + run: wmic logicaldisk get size,freespace,caption + + test-llvm: + needs: [build-llvm] + strategy: + fail-fast: false + matrix: + runs-on: [ubuntu-latest, macos-latest, windows-latest] + build-type: [Debug] + if: always() + runs-on: ${{matrix.runs-on}} + steps: + - name: Uninstall Unused Packages + if: runner.os == 'Linux' + run: sudo apt-get remove ^ghc-[0-9.]+$ + + - name: Checkout Project + uses: actions/checkout@v2 + with: + path: src + - name: Sparse Checkout + working-directory: src + run: | + git version + git sparse-checkout init --cone + git sparse-checkout set llvm/test llvm/utils/lit/lit + + - name: Download Test + uses: actions/download-artifact@v1 + with: + name: Test${{runner.os}}${{matrix.build-type}} + path: build + - name: Download LLVM + uses: actions/download-artifact@v1 + with: + name: LLVM${{runner.os}}${{matrix.build-type}} + path: build + - name: Set Executable + if: runner.os != 'Windows' + run: chmod +x build/bin/* + + - name: Test LLVM + run: build/bin/llvm-lit -v src/llvm/test + + test-clang: + needs: [build-llvm, build-clang] + strategy: + fail-fast: false + matrix: + runs-on: [ubuntu-latest, macos-latest, windows-latest] + build-type: [Debug] + if: always() + runs-on: ${{matrix.runs-on}} + steps: + - name: Uninstall Unused Packages + if: runner.os == 'Linux' + run: sudo apt-get remove ^ghc-[0-9.]+$ + + - name: Checkout Project + uses: actions/checkout@v2 + with: + path: src + - name: Sparse Checkout + working-directory: src + run: | + git version + git sparse-checkout init --cone + git sparse-checkout set clang/test llvm/utils/lit/lit + + - name: Download Test + uses: actions/download-artifact@v1 + with: + name: Test${{runner.os}}${{matrix.build-type}} + path: build + - name: Download LLVM + uses: actions/download-artifact@v1 + with: + name: LLVM${{runner.os}}${{matrix.build-type}} + path: build + - name: Download Clang + uses: actions/download-artifact@v1 + with: + name: Clang${{runner.os}}${{matrix.build-type}} + path: build + - name: Set Executable + if: runner.os != 'Windows' + run: chmod +x build/bin/* + + - name: Test Clang + run: build/bin/llvm-lit -v src/clang/test From 2830cfeaaa2270c43fbb53410405e9c65fd21c39 Mon Sep 17 00:00:00 2001 From: Paul Osmialowski Date: Sat, 28 Mar 2020 23:17:08 +0000 Subject: [PATCH 19/21] Add missing Z80 definitions to ELF binary format. Signed-off-by: Paul Osmialowski --- llvm/include/llvm/BinaryFormat/ELF.h | 18 ++++++++++++++++++ .../llvm/BinaryFormat/ELFRelocs/Z80.def | 19 +++++++++++++++++++ llvm/include/llvm/Object/ELFObjectFile.h | 4 ++++ 3 files changed, 41 insertions(+) create mode 100644 llvm/include/llvm/BinaryFormat/ELFRelocs/Z80.def diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index bdcf10fd1640cd..4e79a8de019c0c 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -307,6 +307,7 @@ enum { EM_COOL = 217, // iCelero CoolEngine EM_NORC = 218, // Nanoradio Optimized RISC EM_CSR_KALIMBA = 219, // CSR Kalimba architecture family + EM_Z80 = 220, // Zilog Z80 EM_AMDGPU = 224, // AMD GPU architecture EM_RISCV = 243, // RISC-V EM_LANAI = 244, // Lanai 32-bit processor @@ -724,6 +725,23 @@ enum : unsigned { EF_AMDGPU_SRAM_ECC = 0x200, }; +// Z80 specific e_flags +enum : unsigned { + EF_Z80_MACH_Z80 = 0x01, + EF_Z80_MACH_Z180 = 0x02, + EF_Z80_MACH_R800 = 0x03, + EF_Z80_MACH_EZ80_Z80 = 0x04, + EF_Z80_MACH_EZ80_ADL = 0x84, + EF_Z80_MACH_GBZ80 = 0x05, + EF_Z80_MACH_Z80N = 0x06, + EF_Z80_MACH_MSK = 0xff +}; + +// ELF Relocation types for Z80 +enum { +#include "ELFRelocs/Z80.def" +}; + // ELF Relocation types for AMDGPU enum { #include "ELFRelocs/AMDGPU.def" diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/Z80.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/Z80.def new file mode 100644 index 00000000000000..38f1d6283880ff --- /dev/null +++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/Z80.def @@ -0,0 +1,19 @@ + +#ifndef ELF_RELOC +#error "ELF_RELOC must be defined" +#endif + +ELF_RELOC(R_Z80_NONE, 0) +ELF_RELOC(R_Z80_8, 1) +ELF_RELOC(R_Z80_8_DIS, 2) +ELF_RELOC(R_Z80_8_PCREL, 3) +ELF_RELOC(R_Z80_16, 4) +ELF_RELOC(R_Z80_24, 5) +ELF_RELOC(R_Z80_32, 6) +ELF_RELOC(R_Z80_BYTE0, 7) +ELF_RELOC(R_Z80_BYTE1, 8) +ELF_RELOC(R_Z80_BYTE2, 9) +ELF_RELOC(R_Z80_BYTE3, 10) +ELF_RELOC(R_Z80_WORD0, 11) +ELF_RELOC(R_Z80_WORD1, 12) +ELF_RELOC(R_Z80_16_BE, 13) diff --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h index 62ecd8b5a7e5cf..ee1bc4863ce5de 100644 --- a/llvm/include/llvm/Object/ELFObjectFile.h +++ b/llvm/include/llvm/Object/ELFObjectFile.h @@ -1097,6 +1097,8 @@ StringRef ELFObjectFile::getFileFormatName() const { return (IsLittleEndian ? "elf32-littlearm" : "elf32-bigarm"); case ELF::EM_AVR: return "elf32-avr"; + case ELF::EM_Z80: + return "elf32-z80"; case ELF::EM_HEXAGON: return "elf32-hexagon"; case ELF::EM_LANAI: @@ -1164,6 +1166,8 @@ template Triple::ArchType ELFObjectFile::getArch() const { return Triple::arm; case ELF::EM_AVR: return Triple::avr; + case ELF::EM_Z80: + return Triple::z80; case ELF::EM_HEXAGON: return Triple::hexagon; case ELF::EM_LANAI: From e1c4140e0c9f1e152fc62882962fb40d50679e01 Mon Sep 17 00:00:00 2001 From: Paul Osmialowski Date: Wed, 22 Apr 2020 12:14:47 +0100 Subject: [PATCH 20/21] Fix things that have hit me hard while working on '-fintegrated-as' Signed-off-by: Paul Osmialowski --- llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp index 078f5e148d69d6..78447584482853 100644 --- a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp @@ -25,7 +25,7 @@ Z80MCAsmInfo::Z80MCAsmInfo(const Triple &T) { DollarIsPC = true; SeparatorString = nullptr; CommentString = ";"; - PrivateGlobalPrefix = PrivateLabelPrefix = ""; + PrivateGlobalPrefix = PrivateLabelPrefix = ".L"; Code16Directive = "assume\tadl = 0"; Code24Directive = "assume\tadl = 1"; Code32Directive = Code64Directive = nullptr; @@ -40,7 +40,6 @@ Z80MCAsmInfo::Z80MCAsmInfo(const Triple &T) { Data64bitsDirective = nullptr; AlwaysChangeSection = true; GlobalDirective = "\tpublic\t"; - LGloblDirective = "\tprivate\t"; SetDirective = "\tlabel\t"; SetSeparator = " at "; HasFunctionAlignment = false; From e9a373b89509567da6e4e901a75f83e2674a749d Mon Sep 17 00:00:00 2001 From: Paul Osmialowski Date: Wed, 22 Apr 2020 12:17:19 +0100 Subject: [PATCH 21/21] ELF code emitter for Z80 architecture (naiive impl.) Some caveat: - The Z80 backend emits a lot of compiler library calls as specified in llvm/lib/Target/Z80/Z80ISelLowering.cpp making it slightly harder for practical use The story: Following the ability of the other 8-bit platform supported by LLVM (AVR) to generate ELF object files, I have prepared this crude ELF code emitter for Z80. It was not extensively tested, mostly due to the lack of a compatible runtime library (e.g. for CP/M system). Yet for the testing purposes, I have prepared something rudimentary, some minimalistic mockup of CP/M's libc allowing me to prepare a simple executable .COM file. It consists of the following files: 1. _bdos.s .text .globl __csave __csave: pop hl ; get the return address push iy ; preserve iy push ix ; preserve ix ld ix, 0 add ix, sp ; new frame pointer jp (hl) ; jump to return address .globl __crestore __crestore: ld sp, ix pop ix ; restore ix pop iy ; restore iy ret ; uint16_t _bdos(uint8_t function, uint16_t arg) .globl __bdos __bdos: call __csave ld c, (ix + 6) ; ix + 6: function ld e, (ix + 8) ; ix + 8: arg (LO) ld d, (ix + 9) ; ix + 9: arg (HI) push iy ; preserve iy push ix ; preserve ix call 0x05 ; call BDOS pop ix ; restore ix pop iy ; restore iy ld l, a ; return the result in l rla ; get the highest bit to the carry flag sbc a, a ; keep the carry flag ld h, a ; return the highest bit in h jp __crestore Compile this with the GNU 'as' from binutils built with --target=z80-none-elf 'configure' flag, e.g.: z80-none-elf-as -o _bdos.o _bdos.s 2. _exit.s .text .globl __Exit __Exit: pop hl ; get rid of the return address pop hl ; exit status ld (0x80), hl ; store exit status at the address 0x80 jp 0 ; return to CP/M Similarly: z80-none-elf-as -o _exit.o _exit.s 3. crtcpm.c _Noreturn void _Exit(int); int main(int, char **); _Noreturn void start(void) __attribute__((no_builtin)) { // TODO: parse whatever there is at 0x80 static char *argv[] = { (char *)(0x80) }; _Exit(main(1, argv)); } Compile this (to 'crtcpm.o') with the LLVM compiler built with -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="Z80", -DLLVM_TARGETS_TO_BUILD="" and -DLLVM_DEFAULT_TARGET_TRIPLE="z80-none-elf" 'cmake' flags, namely: clang -Wall -O0 -fintegrated-as -fomit-frame-pointer -c crtcpm.c 4. main.c uint16_t _bdos(uint8_t, uint16_t); static void putch(int c) { if ('\n' == c) _bdos(0x02U, '\r'); _bdos(0x02U, c); } int main(int argc, char **argv) { int i; int c; const int n = ((unsigned char)(argv[0][0])); for (i = 0; i < n; i++) { c = ((unsigned char)(argv[0][1 + i])); putch('['); if (' ' == c) { putch('s'); putch('p'); } else putch(c); putch(']'); } putch('\n'); return argc; } Similarly, build 'main.o': clang -Wall -fintegrated-as -c main.c Create rudimentary 'libc.a' library from '_bdos.o' and '_exit.o' using GNU 'ar' and 'runlib' from binutils built as described earlier: z80-none-elf-ar r libc.a _bdos.o _exit.o z80-none-elf-ranlib libc.a Link the final .COM file, e.g.: z80-none-elf-ld -o main.com --oformat binary -Ttext=0x100 crtcpm.o main.o -L. -lc Every CP/M program should start at 0x100, hence the .text segment is set to start at that address. The .data segment will be placed right after last byte of the .text segment. Note that 'crtcpm.o' is kept separately from 'libc.a' and is listed as the very first file needed to link the final program. This ensures that the only function defined in 'crtcpm.o' (the _start() function) will be the very first thing placed at the address 0x100. Copy the 'main.com' file to a disk image and execute it in CP/M. Basically, the 'main.com' program should print whatever was given as parameters with each character put into brackets (and spaces printed as [sp]): A>main hello, world [sp][H][E][L][L][O][,][sp][W][O][R][L][D] Note that CP/M turns all the characters in the command line arguments into upper-case. Signed-off-by: Paul Osmialowski --- .../Target/Z80/MCTargetDesc/CMakeLists.txt | 4 + .../Target/Z80/MCTargetDesc/Z80AsmBackend.cpp | 86 + .../Target/Z80/MCTargetDesc/Z80AsmBackend.h | 85 + .../Z80/MCTargetDesc/Z80ELFObjectWriter.cpp | 91 + .../Z80/MCTargetDesc/Z80ELFStreamer.cpp | 27 + .../Target/Z80/MCTargetDesc/Z80ELFStreamer.h | 41 + .../Target/Z80/MCTargetDesc/Z80FixupKinds.h | 48 + .../Z80/MCTargetDesc/Z80MCCodeEmitter.cpp | 5398 +++++++++++++++++ .../Z80/MCTargetDesc/Z80MCCodeEmitter.h | 57 + .../Z80/MCTargetDesc/Z80MCTargetDesc.cpp | 74 +- .../Target/Z80/MCTargetDesc/Z80MCTargetDesc.h | 18 +- 11 files changed, 5918 insertions(+), 11 deletions(-) create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80AsmBackend.cpp create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80AsmBackend.h create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80ELFObjectWriter.cpp create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80ELFStreamer.cpp create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80ELFStreamer.h create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80FixupKinds.h create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80MCCodeEmitter.cpp create mode 100644 llvm/lib/Target/Z80/MCTargetDesc/Z80MCCodeEmitter.h diff --git a/llvm/lib/Target/Z80/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/Z80/MCTargetDesc/CMakeLists.txt index 1939a2c40dcede..35f23d80ecb9ca 100644 --- a/llvm/lib/Target/Z80/MCTargetDesc/CMakeLists.txt +++ b/llvm/lib/Target/Z80/MCTargetDesc/CMakeLists.txt @@ -1,8 +1,12 @@ add_llvm_component_library(LLVMZ80Desc EZ80InstPrinter.cpp + Z80AsmBackend.cpp + Z80ELFObjectWriter.cpp Z80InstPrinter.cpp Z80InstPrinterCommon.cpp Z80MCAsmInfo.cpp + Z80MCCodeEmitter.cpp + Z80ELFStreamer.cpp Z80MCTargetDesc.cpp Z80TargetStreamer.cpp ) diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80AsmBackend.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80AsmBackend.cpp new file mode 100644 index 00000000000000..5ad4eeb5df7da6 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80AsmBackend.cpp @@ -0,0 +1,86 @@ +//===-- Z80AsmBackend.cpp - Z80 Asm Backend ------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the Z80AsmBackend class. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/Z80AsmBackend.h" +#include "MCTargetDesc/Z80FixupKinds.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" + +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDirectives.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCFixupKindInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" + +#include + +namespace llvm { + +std::unique_ptr +Z80AsmBackend::createObjectTargetWriter() const { + return createZ80ELFObjectWriter(ELF::ELFOSABI_STANDALONE); +} + +MCFixupKindInfo const &Z80AsmBackend::getFixupKindInfo(MCFixupKind Kind) const { + const static MCFixupKindInfo Infos[Z80::NumTargetFixupKinds] = { + // This table *must* be in same the order of fixup_* kinds in + // Z80FixupKinds.h. + // + // name offset bits flags + {"fixup_8", 0U, 8U, 0U}, + {"fixup_8_dis", 0U, 8U, 0U}, + {"fixup_8_pcrel", 0U, 8U, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_16", 0U, 16U, 0U}, + {"fixup_24", 0U, 24U, 0U}, + {"fixup_32", 0U, 32U, 0U}, + {"fixup_byte0", 0U, 32U, 0U}, + {"fixup_byte1", 0U, 32U, 0U}, + {"fixup_byte2", 0U, 32U, 0U}, + {"fixup_byte3", 0U, 32U, 0U}, + {"fixup_word0", 0U, 32U, 0U}, + {"fixup_word1", 0U, 32U, 0U}, + {"fixup_16_be", 0U, 16U, 0U}}; + + if (Kind < FirstTargetFixupKind) + return MCAsmBackend::getFixupKindInfo(Kind); + assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && + "Invalid kind!"); + return Infos[Kind - FirstTargetFixupKind]; +} + +bool Z80AsmBackend::shouldForceRelocation(const MCAssembler &Asm, + const MCFixup &Fixup, + const MCValue &Target) { + switch ((unsigned)Fixup.getKind()) { + default: + return false; + // Fixups which should always be recorded as relocations. + case Z80::fixup_8_dis: + case Z80::fixup_8_pcrel: + case Z80::fixup_16: + return true; + } +} + +MCAsmBackend *createZ80AsmBackend(const Target &T, const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const llvm::MCTargetOptions &TO) { + return new Z80AsmBackend(); +} + +} // end of namespace llvm diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80AsmBackend.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80AsmBackend.h new file mode 100644 index 00000000000000..7c66632438a190 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80AsmBackend.h @@ -0,0 +1,85 @@ +//===-- Z80AsmBackend.h - Z80 Asm Backend --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// \file The Z80 assembly backend implementation. +// +//===----------------------------------------------------------------------===// +// + +#ifndef LLVM_Z80_ASM_BACKEND_H +#define LLVM_Z80_ASM_BACKEND_H + +#include "MCTargetDesc/Z80FixupKinds.h" + +#include "llvm/ADT/Triple.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCFixupKindInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include + +namespace llvm { + +class MCAsmLayout; +class MCAssembler; +class MCInst; +class MCObjectWriter; +class MCRelaxableFragment; +class MCSubtargetInfo; +class MCValue; +class Target; + +struct MCFixupKindInfo; + +/// Utilities for manipulating generated Z80 machine code. +class Z80AsmBackend : public MCAsmBackend { +public: + Z80AsmBackend() : MCAsmBackend(support::little) {} + + std::unique_ptr + createObjectTargetWriter() const override; + + void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target, MutableArrayRef Data, + uint64_t Value, bool IsResolved, + const MCSubtargetInfo *STI) const override {} + + const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override; + + unsigned getNumFixupKinds() const override { + return Z80::NumTargetFixupKinds; + } + + bool mayNeedRelaxation(const MCInst &Inst, + const MCSubtargetInfo &STI) const override { + return false; + } + + bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, + const MCRelaxableFragment *DF, + const MCAsmLayout &Layout) const override { + llvm_unreachable("RelaxInstruction() unimplemented"); + return false; + } + + bool writeNopData(raw_ostream &OS, uint64_t Count) const override { + OS.write_zeros(Count); + return true; + } + + bool shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target) override; +}; + +} // end namespace llvm + +#endif // LLVM_Z80_ASM_BACKEND_H diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFObjectWriter.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFObjectWriter.cpp new file mode 100644 index 00000000000000..33448a637d7dcc --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFObjectWriter.cpp @@ -0,0 +1,91 @@ +//===-- Z80ELFObjectWriter.cpp - Z80 ELF Writer ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/Z80FixupKinds.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" + +#include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCSection.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" + +#include +#include + +namespace llvm { + +/// Writes Z80 machine code into an ELF32 object file. +class Z80ELFObjectWriter : public MCELFObjectTargetWriter { +public: + Z80ELFObjectWriter(uint8_t OSABI); + + virtual ~Z80ELFObjectWriter() {} + + unsigned getRelocType(MCContext &Ctx, const MCValue &Target, + const MCFixup &Fixup, bool IsPCRel) const override; +}; + +Z80ELFObjectWriter::Z80ELFObjectWriter(uint8_t OSABI) + : MCELFObjectTargetWriter(false, OSABI, ELF::EM_Z80, true) {} + +unsigned Z80ELFObjectWriter::getRelocType(MCContext &Ctx, const MCValue &Target, + const MCFixup &Fixup, + bool IsPCRel) const { + const MCSymbolRefExpr *SymRef = dyn_cast(Fixup.getValue()); + + switch (static_cast(Fixup.getKind())) { + case FK_Data_1: + case Z80::fixup_8: + return ELF::R_Z80_8; + case Z80::fixup_8_dis: + return ELF::R_Z80_8_DIS; + case Z80::fixup_8_pcrel: + assert(IsPCRel); + return ELF::R_Z80_8_PCREL; + case FK_Data_2: + case Z80::fixup_16: + return ELF::R_Z80_16; + case Z80::fixup_24: + return ELF::R_Z80_24; + case FK_Data_4: + case Z80::fixup_32: + return ELF::R_Z80_32; + case Z80::fixup_byte0: + return ELF::R_Z80_BYTE0; + case Z80::fixup_byte1: + return ELF::R_Z80_BYTE1; + case Z80::fixup_byte2: + return ELF::R_Z80_BYTE2; + case Z80::fixup_byte3: + return ELF::R_Z80_BYTE3; + case Z80::fixup_word0: + return ELF::R_Z80_WORD0; + case Z80::fixup_word1: + return ELF::R_Z80_WORD1; + case Z80::fixup_16_be: + return ELF::R_Z80_16_BE; + default: + llvm_unreachable(Twine("Invalid fixup kind! ", + SymRef ? SymRef->getSymbol().getName().str() + : "(not even a symref!)") + .str() + .c_str()); + } +} + +std::unique_ptr createZ80ELFObjectWriter(uint8_t OSABI) { + return std::make_unique(OSABI); +} + +} // end of namespace llvm diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFStreamer.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFStreamer.cpp new file mode 100644 index 00000000000000..22ce454f6baf8b --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFStreamer.cpp @@ -0,0 +1,27 @@ +//===-- Z80ELFStreamer.cpp - Z80 ELF Streamer -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Z80ELFStreamer.h" + +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/FormattedStream.h" + +namespace llvm { + +Z80ELFStreamer::Z80ELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI) + : Z80TargetStreamer(S) { + MCAssembler &MCA = getStreamer().getAssembler(); + unsigned EFlags = MCA.getELFHeaderEFlags(); + + EFlags |= ELF::EF_Z80_MACH_Z80; + MCA.setELFHeaderEFlags(EFlags); +} + +} // end namespace llvm diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFStreamer.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFStreamer.h new file mode 100644 index 00000000000000..9234797e1e361f --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFStreamer.h @@ -0,0 +1,41 @@ +//===----- Z80ELFStreamer.h - Z80 ELF Streamer -----------------*- C++ -*--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_Z80_ELF_STREAMER_H +#define LLVM_Z80_ELF_STREAMER_H + +#include "Z80TargetStreamer.h" + +#include "llvm/MC/MCDirectives.h" +#include "llvm/MC/MCELFStreamer.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" + +#include + +namespace llvm { + +/// A target streamer for an Z80 ELF object file. +class Z80ELFStreamer : public Z80TargetStreamer { +public: + Z80ELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI); + + MCELFStreamer &getStreamer() { + return static_cast(Streamer); + } + + void emitAlign(unsigned u) override {} + void emitBlock(uint64_t u) override {} + void emitLocal(MCSymbol *S) override {} + void emitGlobal(MCSymbol *S) override {} + void emitExtern(MCSymbol *S) override {} +}; + +} // end namespace llvm + +#endif diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80FixupKinds.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80FixupKinds.h new file mode 100644 index 00000000000000..fcd5e69dd149bf --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80FixupKinds.h @@ -0,0 +1,48 @@ +//===-- Z80FixupKinds.h - Z80 Specific Fixup Entries ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_Z80_FIXUP_KINDS_H +#define LLVM_Z80_FIXUP_KINDS_H + +#include "llvm/MC/MCFixup.h" + +namespace llvm { +namespace Z80 { + +/// The set of supported fixups. +/// +/// Although most of the current fixup types reflect a unique relocation +/// one can have multiple fixup types for a given relocation and thus need +/// to be uniquely named. +/// +/// \note This table *must* be in the same order of +/// MCFixupKindInfo Infos[Z80::NumTargetFixupKinds] +/// in `Z80AsmBackend.cpp`. +enum Fixups { + fixup_8 = FirstTargetFixupKind, + fixup_8_dis, + fixup_8_pcrel, + fixup_16, + fixup_24, + fixup_32, + fixup_byte0, + fixup_byte1, + fixup_byte2, + fixup_byte3, + fixup_word0, + fixup_word1, + fixup_16_be, + // Marker + LastTargetFixupKind, + NumTargetFixupKinds = LastTargetFixupKind - FirstTargetFixupKind +}; + +} // end of namespace Z80 +} // end of namespace llvm + +#endif // LLVM_Z80_FIXUP_KINDS_H diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCCodeEmitter.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCCodeEmitter.cpp new file mode 100644 index 00000000000000..51617c64e263c2 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCCodeEmitter.cpp @@ -0,0 +1,5398 @@ +//===-- Z80MCCodeEmitter.cpp - Convert Z80 Code to Machine Code -----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the Z80MCCodeEmitter class. +// +//===----------------------------------------------------------------------===// + +#include "Z80MCCodeEmitter.h" + +#include "MCTargetDesc/Z80MCTargetDesc.h" + +#include "Z80FixupKinds.h" +#include "Z80InstrInfo.h" +#include "Z80RegisterInfo.h" +#include "Z80Subtarget.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include + +#define DEBUG_TYPE "mccodeemitter" + +#define GET_INSTRMAP_INFO +#include "Z80GenInstrInfo.inc" +#undef GET_INSTRMAP_INFO + +namespace llvm { + +static void check_num_operands(StringRef opc, unsigned expct, unsigned actual) { + if (expct != actual) { + std::string msg; + raw_string_ostream Msg(msg); + Msg << "Invalid number of arguments for instruction " << opc << ": "; + Msg << expct << " vs " << actual << '.'; + report_fatal_error(Msg.str()); + } +} + +static LLVM_ATTRIBUTE_NORETURN void report_fatal_instr_problem(StringRef opc, + const char *er) { + std::string msg; + raw_string_ostream Msg(msg); + Msg << opc << ": " << er; + report_fatal_error(Msg.str()); +} + +void Z80MCCodeEmitter::emitByte(uint8_t C, unsigned &CurByte, + raw_ostream &OS) const { + OS << (static_cast(C)); + ++CurByte; +} + +void Z80MCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + // Keep track of the current byte being emitted. + unsigned CurByte = 0U; + + const unsigned opcode = MI.getOpcode(); + const MCInstrDesc &Desc = MCII.get(opcode); + const unsigned numOperands = Desc.getNumOperands(); + const MCInst::const_iterator Operands = MI.begin(); + + if (Z80II::EZ80Mode == ((Desc.TSFlags) & Z80II::ModeMask)) { + std::string msg; + raw_string_ostream Msg(msg); + Msg << "EZ80 machine instructions not supported (yet?)"; + report_fatal_error(Msg.str()); + } + if (Desc.isPseudo()) { + switch (opcode) { + case Z80::JQ: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isExpr())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be an expression."); +#ifdef EMIT_JR_INSTEAD_OF_JP + emitByte(0x18U, CurByte, OS); + Fixups.push_back(MCFixup::create( + CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_8_pcrel), MI.getLoc())); + emitByte(0x00U, CurByte, OS); +#else + emitByte(0xc3U, CurByte, OS); + Fixups.push_back(MCFixup::create(CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); +#endif + break; + case Z80::JQCC: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isExpr())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be an expression."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); +#ifdef EMIT_JRCC_INSTEAD_OF_JPCC + if (4U <= static_cast(Operands[1].getImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be in range 0..3."); +#else + if (8U <= static_cast(Operands[1].getImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be in range 0..7."); +#endif +#ifdef EMIT_JRCC_INSTEAD_OF_JPCC + emitByte((static_cast(Operands[1].getImm()) << 3U) | 0x20U, + CurByte, OS); + Fixups.push_back(MCFixup::create( + CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_8_pcrel), MI.getLoc())); + emitByte(0x00U, CurByte, OS); +#else + emitByte((static_cast(Operands[1].getImm()) << 3U) | 0xc2U, + CurByte, OS); + Fixups.push_back(MCFixup::create(CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); +#endif + break; + default: + std::string msg; + raw_string_ostream Msg(msg); + Msg << "Not supported pseudo instr: " << MI; + report_fatal_error(Msg.str()); + } + return; + } + switch (opcode) { + case Z80::ADC8ai: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + emitByte(0xceU, CurByte, OS); + emitByte(Operands[0].getImm(), CurByte, OS); + break; + case Z80::ADC8ao: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0x8eU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::ADC8ap: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0x8eU, CurByte, OS); + break; + case Z80::ADC8ar: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x8fU, CurByte, OS); + break; + case Z80::B: + emitByte(0x88U, CurByte, OS); + break; + case Z80::C: + emitByte(0x89U, CurByte, OS); + break; + case Z80::D: + emitByte(0x8aU, CurByte, OS); + break; + case Z80::E: + emitByte(0x8bU, CurByte, OS); + break; + case Z80::H: + emitByte(0x8cU, CurByte, OS); + break; + case Z80::L: + emitByte(0x8dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x8cU, CurByte, OS); // ADC A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x8dU, CurByte, OS); // ADC A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x8cU, CurByte, OS); // ADC A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x8dU, CurByte, OS); // ADC A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::ADD16SP: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be registers."); + if ((Operands[0].getReg()) != (Operands[1].getReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be the same register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte(0x39U, CurByte, OS); + break; + case Z80::ADD16aa: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be registers."); + if ((Operands[0].getReg()) != (Operands[1].getReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be the same register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte(0x29U, CurByte, OS); + break; + case Z80::ADD16ao: + check_num_operands(MCII.getName(opcode), 3U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()) && + (Operands[2].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "All operands should be registers."); + if ((Operands[0].getReg()) != (Operands[1].getReg())) + report_fatal_instr_problem( + MCII.getName(opcode), + "First two of the operands should be the same register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed first two registers are HL, IX, IY."); + } + switch (Operands[2].getReg()) { + case Z80::BC: + emitByte(0x09U, CurByte, OS); + break; + case Z80::DE: + emitByte(0x19U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed last registers are BC, DE."); + } + break; + case Z80::ADD8ai: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + emitByte(0xc6U, CurByte, OS); + emitByte(Operands[0].getImm(), CurByte, OS); + break; + case Z80::ADD8ao: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0x86U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::ADD8ap: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0x86U, CurByte, OS); + break; + case Z80::ADD8ar: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x87U, CurByte, OS); + break; + case Z80::B: + emitByte(0x80U, CurByte, OS); + break; + case Z80::C: + emitByte(0x81U, CurByte, OS); + break; + case Z80::D: + emitByte(0x82U, CurByte, OS); + break; + case Z80::E: + emitByte(0x83U, CurByte, OS); + break; + case Z80::H: + emitByte(0x84U, CurByte, OS); + break; + case Z80::L: + emitByte(0x85U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x84U, CurByte, OS); // ADD A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x85U, CurByte, OS); // ADD A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x84U, CurByte, OS); // ADD A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x85U, CurByte, OS); // ADD A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::AND8ai: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + emitByte(0xe6U, CurByte, OS); + emitByte(Operands[0].getImm(), CurByte, OS); + break; + case Z80::AND8ao: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xa6U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::AND8ap: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xa6U, CurByte, OS); + break; + case Z80::AND8ar: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xa7U, CurByte, OS); + break; + case Z80::B: + emitByte(0xa0U, CurByte, OS); + break; + case Z80::C: + emitByte(0xa1U, CurByte, OS); + break; + case Z80::D: + emitByte(0xa2U, CurByte, OS); + break; + case Z80::E: + emitByte(0xa3U, CurByte, OS); + break; + case Z80::H: + emitByte(0xa4U, CurByte, OS); + break; + case Z80::L: + emitByte(0xa5U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xa4U, CurByte, OS); // AND A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xa5U, CurByte, OS); // AND A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xa4U, CurByte, OS); // AND A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xa5U, CurByte, OS); // AND A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::BIT8bg: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + if (8U <= static_cast(Operands[0].getImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + switch (Operands[1].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x47U, + CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x40U, + CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x41U, + CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x42U, + CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x43U, + CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x44U, + CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x45U, + CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x44U, + CurByte, OS); // BIT b, H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x45U, + CurByte, OS); // BIT b, L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x44U, + CurByte, OS); // BIT b, H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x45U, + CurByte, OS); // BIT b, L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::BIT8bo: + check_num_operands(MCII.getName(opcode), 3U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + if (!(Operands[2].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Third operand should be immediate."); + if (8U <= static_cast(Operands[0].getImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x46, CurByte, + OS); + break; + case Z80::BIT8bp: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + if (8U <= static_cast(Operands[0].getImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + switch (Operands[1].getReg()) { + case Z80::HL: + emitByte(0xcbU, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0xcbU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0xcbU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x46U, + CurByte, OS); + break; + case Z80::CALL16: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + emitByte(0xcdU, CurByte, OS); + if (Operands[0].isExpr()) { + Fixups.push_back(MCFixup::create(CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[0].isImm()) { + emitByte(((static_cast(Operands[0].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[0].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "Operand should be an expression or immediate."); + } + break; + case Z80::CALL16CC: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + if (8U <= static_cast(Operands[1].getImm())) { + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be in range 0..7."); + } + emitByte((static_cast(Operands[1].getImm()) << 3U) | 0xc4U, + CurByte, OS); + if (Operands[0].isExpr()) { + if (!(MCExpr::SymbolRef == (Operands[0].getExpr()->getKind()))) + report_fatal_instr_problem( + MCII.getName(opcode), + "Fitst operand expression should be a call target."); + Fixups.push_back(MCFixup::create(CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[0].isImm()) { + emitByte(((static_cast(Operands[0].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[0].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "First operand should be an expression or immediate."); + } + break; + case Z80::CCF: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0x3fU, CurByte, OS); + break; + case Z80::CP8ai: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + emitByte(0xfeU, CurByte, OS); + emitByte(Operands[0].getImm(), CurByte, OS); + break; + case Z80::CP8ao: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xbeU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::CP8ap: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xbeU, CurByte, OS); + break; + case Z80::CP8ar: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xbfU, CurByte, OS); + break; + case Z80::B: + emitByte(0xb8U, CurByte, OS); + break; + case Z80::C: + emitByte(0xb9U, CurByte, OS); + break; + case Z80::D: + emitByte(0xbaU, CurByte, OS); + break; + case Z80::E: + emitByte(0xbbU, CurByte, OS); + break; + case Z80::H: + emitByte(0xbcU, CurByte, OS); + break; + case Z80::L: + emitByte(0xbdU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xbcU, CurByte, OS); // CP A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xbdU, CurByte, OS); // CP A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xbcU, CurByte, OS); // CP A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xbdU, CurByte, OS); // CP A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::CPD16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xa9U, CurByte, OS); + break; + case Z80::CPDR16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xb9U, CurByte, OS); + break; + case Z80::CPI16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xa1U, CurByte, OS); + break; + case Z80::CPIR16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xb1U, CurByte, OS); + break; + case Z80::CPL: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0x2fU, CurByte, OS); + break; + case Z80::DEC16SP: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0x3bU, CurByte, OS); + break; + case Z80::DEC16r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::BC: + emitByte(0x0bU, CurByte, OS); + break; + case Z80::DE: + emitByte(0x1bU, CurByte, OS); + break; + case Z80::HL: + emitByte(0x2bU, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0x2bU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0x2bU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are BC, DE, HL, IX, IY."); + } + break; + case Z80::DEC8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0x35U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::DEC8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + emitByte(0x35U, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0x35U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0x35U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + break; + case Z80::DEC8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x3dU, CurByte, OS); + break; + case Z80::B: + emitByte(0x05U, CurByte, OS); + break; + case Z80::C: + emitByte(0x0dU, CurByte, OS); + break; + case Z80::D: + emitByte(0x15U, CurByte, OS); + break; + case Z80::E: + emitByte(0x1dU, CurByte, OS); + break; + case Z80::H: + emitByte(0x25U, CurByte, OS); + break; + case Z80::L: + emitByte(0x2dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x25U, CurByte, OS); // DEC H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x2dU, CurByte, OS); // DEC L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x25U, CurByte, OS); // DEC H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x2dU, CurByte, OS); // DEC L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::DI: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xf3U, CurByte, OS); + break; + case Z80::EI: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xfbU, CurByte, OS); + break; + case Z80::EX16DE: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xebU, CurByte, OS); + break; + case Z80::EX16SP: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be registers."); + if ((Operands[0].getReg()) != (Operands[1].getReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be the same register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte(0xe3U, CurByte, OS); + break; + case Z80::EXAF: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0x08U, CurByte, OS); + break; + case Z80::EXX: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xd9U, CurByte, OS); + break; + case Z80::INC16SP: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0x33U, CurByte, OS); + break; + case Z80::INC16r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::BC: + emitByte(0x03U, CurByte, OS); + break; + case Z80::DE: + emitByte(0x13U, CurByte, OS); + break; + case Z80::HL: + emitByte(0x23U, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0x23U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0x23U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are BC, DE, HL, IX, IY."); + } + break; + case Z80::INC8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0x34U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::INC8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + emitByte(0x34U, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0x34U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0x34U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + break; + case Z80::INC8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x3cU, CurByte, OS); + break; + case Z80::B: + emitByte(0x04U, CurByte, OS); + break; + case Z80::C: + emitByte(0x0cU, CurByte, OS); + break; + case Z80::D: + emitByte(0x14U, CurByte, OS); + break; + case Z80::E: + emitByte(0x1cU, CurByte, OS); + break; + case Z80::H: + emitByte(0x24U, CurByte, OS); + break; + case Z80::L: + emitByte(0x2cU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x24U, CurByte, OS); // INC H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x2cU, CurByte, OS); // INC L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x24U, CurByte, OS); // INC H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x2cU, CurByte, OS); // INC L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::IND16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xaaU, CurByte, OS); + break; + case Z80::INDR16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xbaU, CurByte, OS); + break; + case Z80::INI16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xa2U, CurByte, OS); + break; + case Z80::INIR16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xb2U, CurByte, OS); + break; + case Z80::JP16r: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte(0xe9U, CurByte, OS); + break; + case Z80::LD16SP: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte(0xf9U, CurByte, OS); + break; + case Z80::LD16am: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte(0x2aU, CurByte, OS); + if (Operands[1].isExpr()) { + Fixups.push_back(MCFixup::create(CurByte, Operands[1].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[1].isImm()) { + emitByte(((static_cast(Operands[1].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[1].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "Second operand should be an expression or immediate."); + } + break; + case Z80::LD16ma: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + switch (Operands[1].getReg()) { + case Z80::HL: + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte(0x22U, CurByte, OS); + if (Operands[0].isExpr()) { + Fixups.push_back(MCFixup::create(CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[0].isImm()) { + emitByte(((static_cast(Operands[0].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[0].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "First operand should be an expression or immediate."); + } + break; + case Z80::LD16mo: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + switch (Operands[1].getReg()) { + case Z80::BC: + emitByte(0xedU, CurByte, OS); + emitByte(0x43U, CurByte, OS); + break; + case Z80::DE: + emitByte(0xedU, CurByte, OS); + emitByte(0x53U, CurByte, OS); + break; + case Z80::HL: + emitByte(0xedU, CurByte, OS); + emitByte(0x63U, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0x22U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0x22U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are BC, DE, HL, IX, IY."); + } + if (Operands[0].isExpr()) { + Fixups.push_back(MCFixup::create(CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[0].isImm()) { + emitByte(((static_cast(Operands[0].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[0].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "First operand should be an expression or immediate."); + } + break; + case Z80::LD16om: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + switch (Operands[0].getReg()) { + case Z80::BC: + emitByte(0xedU, CurByte, OS); + emitByte(0x4bU, CurByte, OS); + break; + case Z80::DE: + emitByte(0xedU, CurByte, OS); + emitByte(0x5bU, CurByte, OS); + break; + case Z80::HL: + emitByte(0xedU, CurByte, OS); + emitByte(0x6bU, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0x2aU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0x2aU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are BC, DE, HL, IX, IY."); + } + if (Operands[1].isExpr()) { + Fixups.push_back(MCFixup::create(CurByte, Operands[1].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[1].isImm()) { + emitByte(((static_cast(Operands[1].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[1].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "Second operand should be an expression or immediate."); + } + break; + case Z80::LD16ri: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + switch (Operands[0].getReg()) { + case Z80::BC: + emitByte(0x01U, CurByte, OS); + break; + case Z80::DE: + emitByte(0x11U, CurByte, OS); + break; + case Z80::HL: + emitByte(0x21U, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0x21U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0x21U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are BC, DE, HL, IX, IY."); + } + if (Operands[1].isExpr()) { + Fixups.push_back(MCFixup::create(CurByte, Operands[1].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[1].isImm()) { + emitByte(((static_cast(Operands[1].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[1].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "Second operand should be an expression or immediate."); + } + break; + case Z80::LD8am: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + emitByte(0x3aU, CurByte, OS); + if (Operands[0].isExpr()) { + Fixups.push_back(MCFixup::create(CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[0].isImm()) { + emitByte(((static_cast(Operands[0].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[0].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "Operand should be an expression or immediate."); + } + break; + case Z80::LD8gg: + case Z80::LD8xx: + case Z80::LD8yy: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be registers."); + switch (Operands[0].getReg()) { /* OUT: r */ + case Z80::A: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0x7fU, CurByte, OS); + break; + case Z80::B: + emitByte(0x78U, CurByte, OS); + break; + case Z80::C: + emitByte(0x79U, CurByte, OS); + break; + case Z80::D: + emitByte(0x7aU, CurByte, OS); + break; + case Z80::E: + emitByte(0x7bU, CurByte, OS); + break; + case Z80::H: + emitByte(0x7cU, CurByte, OS); + break; + case Z80::L: + emitByte(0x7dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x7cU, CurByte, OS); // LD A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x7dU, CurByte, OS); // LD A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x7cU, CurByte, OS); // LD A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x7dU, CurByte, OS); // LD A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::B: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0x47U, CurByte, OS); + break; + case Z80::B: + emitByte(0x40U, CurByte, OS); + break; + case Z80::C: + emitByte(0x41U, CurByte, OS); + break; + case Z80::D: + emitByte(0x42U, CurByte, OS); + break; + case Z80::E: + emitByte(0x43U, CurByte, OS); + break; + case Z80::H: + emitByte(0x44U, CurByte, OS); + break; + case Z80::L: + emitByte(0x45U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x44U, CurByte, OS); // LD B, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x45U, CurByte, OS); // LD B, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x44U, CurByte, OS); // LD B, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x45U, CurByte, OS); // LD B, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::C: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0x4fU, CurByte, OS); + break; + case Z80::B: + emitByte(0x48U, CurByte, OS); + break; + case Z80::C: + emitByte(0x49U, CurByte, OS); + break; + case Z80::D: + emitByte(0x4aU, CurByte, OS); + break; + case Z80::E: + emitByte(0x4bU, CurByte, OS); + break; + case Z80::H: + emitByte(0x4cU, CurByte, OS); + break; + case Z80::L: + emitByte(0x4dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x4cU, CurByte, OS); // LD C, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x4dU, CurByte, OS); // LD C, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x4cU, CurByte, OS); // LD C, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x4dU, CurByte, OS); // LD C, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::D: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0x57U, CurByte, OS); + break; + case Z80::B: + emitByte(0x50U, CurByte, OS); + break; + case Z80::C: + emitByte(0x51U, CurByte, OS); + break; + case Z80::D: + emitByte(0x52U, CurByte, OS); + break; + case Z80::E: + emitByte(0x53U, CurByte, OS); + break; + case Z80::H: + emitByte(0x54U, CurByte, OS); + break; + case Z80::L: + emitByte(0x55U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x54U, CurByte, OS); // LD D, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x55U, CurByte, OS); // LD D, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x54U, CurByte, OS); // LD D, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x55U, CurByte, OS); // LD D, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::E: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0x5fU, CurByte, OS); + break; + case Z80::B: + emitByte(0x58U, CurByte, OS); + break; + case Z80::C: + emitByte(0x59U, CurByte, OS); + break; + case Z80::D: + emitByte(0x5aU, CurByte, OS); + break; + case Z80::E: + emitByte(0x5bU, CurByte, OS); + break; + case Z80::H: + emitByte(0x5cU, CurByte, OS); + break; + case Z80::L: + emitByte(0x5dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x5cU, CurByte, OS); // LD E, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x5dU, CurByte, OS); // LD E, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x5cU, CurByte, OS); // LD E, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x5dU, CurByte, OS); // LD E, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::H: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0x67U, CurByte, OS); + break; + case Z80::B: + emitByte(0x60U, CurByte, OS); + break; + case Z80::C: + emitByte(0x61U, CurByte, OS); + break; + case Z80::D: + emitByte(0x62U, CurByte, OS); + break; + case Z80::E: + emitByte(0x63U, CurByte, OS); + break; + case Z80::H: + emitByte(0x64U, CurByte, OS); + break; + case Z80::L: + emitByte(0x65U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x62U, CurByte, OS); // LD H, D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x63U, CurByte, OS); // LD H, E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x62U, CurByte, OS); // LD H, D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x63U, CurByte, OS); // LD H, E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::L: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0x6fU, CurByte, OS); + break; + case Z80::B: + emitByte(0x68U, CurByte, OS); + break; + case Z80::C: + emitByte(0x69U, CurByte, OS); + break; + case Z80::D: + emitByte(0x6aU, CurByte, OS); + break; + case Z80::E: + emitByte(0x6bU, CurByte, OS); + break; + case Z80::H: + emitByte(0x6cU, CurByte, OS); + break; + case Z80::L: + emitByte(0x6dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x6aU, CurByte, OS); // LD L, D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x6bU, CurByte, OS); // LD L, E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x6aU, CurByte, OS); // LD L, D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x6bU, CurByte, OS); // LD L, E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::IXH: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x67U, CurByte, OS); // LD H, A + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::B: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x60U, CurByte, OS); // LD H, B + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::C: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x61U, CurByte, OS); // LD H, C + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::D: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x62U, CurByte, OS); // LD H, D + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::E: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x63U, CurByte, OS); // LD H, E + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::H: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x54U, CurByte, OS); // LD D, H + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::L: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x55U, CurByte, OS); // LD D, L + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x64U, CurByte, OS); // LD H, H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x65U, CurByte, OS); // LD H, L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x62U, CurByte, OS); // LD H, D + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x63U, CurByte, OS); // LD H, E + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::IXL: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6fU, CurByte, OS); // LD L, A + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::B: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x68U, CurByte, OS); // LD L, B + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::C: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x69U, CurByte, OS); // LD L, C + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::D: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6aU, CurByte, OS); // LD L, D + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::E: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6bU, CurByte, OS); // LD L, E + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::H: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x5cU, CurByte, OS); // LD E, H + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::L: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x5dU, CurByte, OS); // LD E, L + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6cU, CurByte, OS); // LD L, H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6dU, CurByte, OS); // LD L, L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x6aU, CurByte, OS); // LD L, D + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x6bU, CurByte, OS); // LD L, E + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::IYH: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x67U, CurByte, OS); // LD H, A + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::B: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x60U, CurByte, OS); // LD H, B + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::C: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x61U, CurByte, OS); // LD H, C + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::D: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x62U, CurByte, OS); // LD H, D + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::E: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x63U, CurByte, OS); // LD H, E + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::H: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x54U, CurByte, OS); // LD D, H + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::L: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x55U, CurByte, OS); // LD D, L + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x62U, CurByte, OS); // LD H, D + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x63U, CurByte, OS); // LD H, E + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x64U, CurByte, OS); // LD H, H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x65U, CurByte, OS); // LD H, L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::IYL: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6fU, CurByte, OS); // LD L, A + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::B: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x68U, CurByte, OS); // LD L, B + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::C: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x69U, CurByte, OS); // LD L, C + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::D: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6aU, CurByte, OS); // LD L, D + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::E: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6bU, CurByte, OS); // LD L, E + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::H: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x5cU, CurByte, OS); // LD E, H + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::L: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x5dU, CurByte, OS); // LD E, L + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x6aU, CurByte, OS); // LD L, D + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x6bU, CurByte, OS); // LD L, E + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6cU, CurByte, OS); // LD L, H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6dU, CurByte, OS); // LD L, L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::LD8go: + check_num_operands(MCII.getName(opcode), 3U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "First two operands should be registers."); + if (!(Operands[2].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Third operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed second operand registers are IX, IY."); + } + emitByte(0x66U, CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); // LD H, (IX|Y + Imm) + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed second operand registers are IX, IY."); + } + emitByte(0x6eU, CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); // LD L, (IX|Y + Imm) + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed second operand registers are IX, IY."); + } + emitByte(0x66, CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); // LD H, (IX|Y + Imm) + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed second operand registers are IX, IY."); + } + emitByte(0x6eU, CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); // LD L, (IX|Y + Imm) + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed second operand registers are IX, IY."); + } + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x7eU, CurByte, OS); + break; + case Z80::B: + emitByte(0x46U, CurByte, OS); + break; + case Z80::C: + emitByte(0x4eU, CurByte, OS); + break; + case Z80::D: + emitByte(0x56U, CurByte, OS); + break; + case Z80::E: + emitByte(0x5eU, CurByte, OS); + break; + case Z80::H: + emitByte(0x66U, CurByte, OS); + break; + case Z80::L: + emitByte(0x6eU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are A, B, C, D, E, H, L."); + } + emitByte(Operands[2].getImm(), CurByte, OS); + } + break; + case Z80::LD8gp: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be registers."); + switch (Operands[1].getReg()) { + case Z80::HL: + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x7eU, CurByte, OS); + break; + case Z80::B: + emitByte(0x46U, CurByte, OS); + break; + case Z80::C: + emitByte(0x4eU, CurByte, OS); + break; + case Z80::D: + emitByte(0x56U, CurByte, OS); + break; + case Z80::E: + emitByte(0x5eU, CurByte, OS); + break; + case Z80::H: + emitByte(0x66U, CurByte, OS); + break; + case Z80::L: + emitByte(0x6eU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x56U, CurByte, OS); // LD D, (HL) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x5eU, CurByte, OS); // LD E, (HL) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x56U, CurByte, OS); // LD D, (HL) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x5eU, CurByte, OS); // LD E, (HL) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are A, B, C, D, E, H, L."); + } + break; + case Z80::IX: + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xddU, CurByte, OS); + emitByte(0x7eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::B: + emitByte(0xddU, CurByte, OS); + emitByte(0x46U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::C: + emitByte(0xddU, CurByte, OS); + emitByte(0x4eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::D: + emitByte(0xddU, CurByte, OS); + emitByte(0x56U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::E: + emitByte(0xddU, CurByte, OS); + emitByte(0x5eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::H: + emitByte(0xddU, CurByte, OS); + emitByte(0x66U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::L: + emitByte(0xddU, CurByte, OS); + emitByte(0x6eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xddU, CurByte, OS); + emitByte(0x56U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD D, (IX+0) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xddU, CurByte, OS); + emitByte(0x5eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD E, (IX+0) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xddU, CurByte, OS); + emitByte(0x56U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD D, (IX+0) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xddU, CurByte, OS); + emitByte(0x5eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD E, (IX+0) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are A, B, C, D, E, H, L."); + } + break; + case Z80::IY: + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xfdU, CurByte, OS); + emitByte(0x7eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::B: + emitByte(0xfdU, CurByte, OS); + emitByte(0x46U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::C: + emitByte(0xfdU, CurByte, OS); + emitByte(0x4eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::D: + emitByte(0xfdU, CurByte, OS); + emitByte(0x56U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::E: + emitByte(0xfdU, CurByte, OS); + emitByte(0x5eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::H: + emitByte(0xfdU, CurByte, OS); + emitByte(0x66U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::L: + emitByte(0xfdU, CurByte, OS); + emitByte(0x6eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xfdU, CurByte, OS); + emitByte(0x56U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD D, (IY+0) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xfdU, CurByte, OS); + emitByte(0x5eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD E, (IY+0) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xfdU, CurByte, OS); + emitByte(0x56U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD D, (IY+0) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xfdU, CurByte, OS); + emitByte(0x5eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD E, (IY+0) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are A, B, C, D, E, H, L."); + } + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + break; + case Z80::LD8ma: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + emitByte(0x32U, CurByte, OS); + if (Operands[0].isExpr()) { + Fixups.push_back(MCFixup::create(CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[0].isImm()) { + emitByte(((static_cast(Operands[0].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[0].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "Operand should be an expression or immediate."); + } + break; + case Z80::LD8og: + check_num_operands(MCII.getName(opcode), 3U, numOperands); + if (!((Operands[0].isReg()) && (Operands[2].isReg()))) + report_fatal_instr_problem( + MCII.getName(opcode), "First and third operand should be registers."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[2].getReg()) { + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are IX, IY."); + } + emitByte(0x74U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); // LD (IX|Y + Imm), H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are IX, IY."); + } + emitByte(0x75U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); // LD (IX|Y + Imm), L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are IX, IY."); + } + emitByte(0x74U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); // LD (IX|Y + Imm), H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are IX, IY."); + } + emitByte(0x75U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); // LD (IX|Y + Imm), L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are IX, IY."); + } + switch (Operands[2].getReg()) { + case Z80::A: + emitByte(0x77U, CurByte, OS); + break; + case Z80::B: + emitByte(0x70U, CurByte, OS); + break; + case Z80::C: + emitByte(0x71U, CurByte, OS); + break; + case Z80::D: + emitByte(0x72U, CurByte, OS); + break; + case Z80::E: + emitByte(0x73U, CurByte, OS); + break; + case Z80::H: + emitByte(0x74U, CurByte, OS); + break; + case Z80::L: + emitByte(0x75U, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed third operand registers are A, B, C, D, E, H, L."); + } + emitByte(Operands[1].getImm(), CurByte, OS); + } + break; + case Z80::LD8oi: + check_num_operands(MCII.getName(opcode), 3U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!((Operands[1].isImm()) && (Operands[2].isImm()))) + report_fatal_instr_problem( + MCII.getName(opcode), + "Second and third operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0x36U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); + break; + case Z80::LD8pg: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be registers."); + switch (Operands[0].getReg()) { + case Z80::HL: + switch (Operands[1].getReg()) { + case Z80::A: + emitByte(0x77U, CurByte, OS); + break; + case Z80::B: + emitByte(0x70U, CurByte, OS); + break; + case Z80::C: + emitByte(0x71U, CurByte, OS); + break; + case Z80::D: + emitByte(0x72U, CurByte, OS); + break; + case Z80::E: + emitByte(0x73U, CurByte, OS); + break; + case Z80::H: + emitByte(0x74U, CurByte, OS); + break; + case Z80::L: + emitByte(0x75U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x72U, CurByte, OS); // LD (HL), D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x73U, CurByte, OS); // LD (HL), E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x72U, CurByte, OS); // LD (HL), D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x73U, CurByte, OS); // LD (HL), E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are A, B, C, D, E, H, L."); + } + break; + case Z80::IX: + switch (Operands[1].getReg()) { + case Z80::A: + emitByte(0xddU, CurByte, OS); + emitByte(0x77U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::B: + emitByte(0xddU, CurByte, OS); + emitByte(0x70U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::C: + emitByte(0xddU, CurByte, OS); + emitByte(0x71U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::D: + emitByte(0xddU, CurByte, OS); + emitByte(0x72U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::E: + emitByte(0xddU, CurByte, OS); + emitByte(0x73U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::H: + emitByte(0xddU, CurByte, OS); + emitByte(0x74U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::L: + emitByte(0xddU, CurByte, OS); + emitByte(0x75U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xddU, CurByte, OS); + emitByte(0x72U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD (IX+0), D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xddU, CurByte, OS); + emitByte(0x73U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD (IX+0), E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xddU, CurByte, OS); + emitByte(0x72U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD (IX+0), D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xddU, CurByte, OS); + emitByte(0x73U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD (IX+0), E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are A, B, C, D, E, H, L."); + } + break; + case Z80::IY: + switch (Operands[1].getReg()) { + case Z80::A: + emitByte(0xfdU, CurByte, OS); + emitByte(0x77U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::B: + emitByte(0xfdU, CurByte, OS); + emitByte(0x70U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::C: + emitByte(0xfdU, CurByte, OS); + emitByte(0x71U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::D: + emitByte(0xfdU, CurByte, OS); + emitByte(0x72U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::E: + emitByte(0xfdU, CurByte, OS); + emitByte(0x73U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::H: + emitByte(0xfdU, CurByte, OS); + emitByte(0x74U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::L: + emitByte(0xfdU, CurByte, OS); + emitByte(0x75U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xfdU, CurByte, OS); + emitByte(0x72U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD (IY+0), D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xfdU, CurByte, OS); + emitByte(0x73U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD (IY+0), E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xfdU, CurByte, OS); + emitByte(0x72U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD (IY+0), D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xfdU, CurByte, OS); + emitByte(0x73U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD (IY+0), E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are A, B, C, D, E, H, L."); + } + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + break; + case Z80::LD8ri: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x3eU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::B: + emitByte(0x06U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::C: + emitByte(0x0eU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::D: + emitByte(0x16U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::E: + emitByte(0x1eU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::H: + emitByte(0x26U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::L: + emitByte(0x2eU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x26U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); // LD H, Imm + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x2eU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); // LD L, Imm + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x26U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); // LD H, Imm + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x2eU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); // LD L, Imm + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are A, B, C, D, E, H, L."); + } + break; + case Z80::LD8pi: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::HL: + emitByte(0x36U, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0x36U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0x36U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::LDD16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xa8U, CurByte, OS); + break; + case Z80::LDDR16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xb8U, CurByte, OS); + break; + case Z80::LDI16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xa0U, CurByte, OS); + break; + case Z80::LDIR16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xb0U, CurByte, OS); + break; + case Z80::LEA16ro: + check_num_operands(MCII.getName(opcode), 3U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "First two operands should be registers."); + if (!(Operands[2].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Third operand should be immediate."); + emitByte(0xf5U, CurByte, OS); // PUSH AF + if (Z80::BC != Operands[0].getReg()) + emitByte(0xc5U, CurByte, OS); // PUSH BC + emitByte(0x06U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD B, 0 + emitByte(0x0eU, CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); // LD C, Imm + switch (Operands[1].getReg()) { + case Z80::IX: + if ((Operands[0].getReg()) != (Operands[1].getReg())) { + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + } + emitByte(0xddU, CurByte, OS); + emitByte(0x09U, CurByte, OS); // ADD IX, BC + if ((Operands[0].getReg()) != (Operands[1].getReg())) { + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + } + break; + case Z80::IY: + if ((Operands[0].getReg()) != (Operands[1].getReg())) { + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + } + emitByte(0xfdU, CurByte, OS); + emitByte(0x09U, CurByte, OS); // ADD IY, BC + if ((Operands[0].getReg()) != (Operands[1].getReg())) { + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + } + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed registers in the second operand are IX, IY."); + } + if ((Operands[0].getReg()) != (Operands[1].getReg())) { + switch (Operands[0].getReg()) { + case Z80::BC: + emitByte(0xc1U, CurByte, OS); // POP BC + break; + case Z80::DE: + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::HL: + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed registers in the first operand are BC, DE, HL, IX, IY."); + } + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed registers in the second operand are IX, IY."); + } + } + if (Z80::BC != Operands[0].getReg()) + emitByte(0xc1U, CurByte, OS); // POP BC + emitByte(0xf1U, CurByte, OS); // POP AF + break; + case Z80::NEG: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0x44U, CurByte, OS); + break; + case Z80::NOP: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0x00U, CurByte, OS); + break; + case Z80::OR8ai: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + emitByte(0xf6U, CurByte, OS); + emitByte(Operands[0].getImm(), CurByte, OS); + break; + case Z80::OR8ao: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xb6U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::OR8ap: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xb6U, CurByte, OS); + break; + case Z80::OR8ar: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xb7U, CurByte, OS); + break; + case Z80::B: + emitByte(0xb0U, CurByte, OS); + break; + case Z80::C: + emitByte(0xb1U, CurByte, OS); + break; + case Z80::D: + emitByte(0xb2U, CurByte, OS); + break; + case Z80::E: + emitByte(0xb3U, CurByte, OS); + break; + case Z80::H: + emitByte(0xb4U, CurByte, OS); + break; + case Z80::L: + emitByte(0xb5U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xb4U, CurByte, OS); // OR A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xb5U, CurByte, OS); // OR A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xb4U, CurByte, OS); // OR A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xb5U, CurByte, OS); // OR A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::OUTD16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xabU, CurByte, OS); + break; + case Z80::OUTDR16: /* OTDR */ + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xbbU, CurByte, OS); + break; + case Z80::OUTI16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xa3U, CurByte, OS); + break; + case Z80::OUTIR16: /* OTIR */ + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xb3U, CurByte, OS); + break; + case Z80::POP16AF: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xf1U, CurByte, OS); + break; + case Z80::POP16r: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::BC: + emitByte(0xc1U, CurByte, OS); + break; + case Z80::DE: + emitByte(0xd1U, CurByte, OS); + break; + case Z80::HL: + emitByte(0xe1U, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are BC, DE, HL, IX, IY."); + } + break; + case Z80::PUSH16AF: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xf5U, CurByte, OS); + break; + case Z80::PUSH16r: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::BC: + emitByte(0xc5U, CurByte, OS); + break; + case Z80::DE: + emitByte(0xd5U, CurByte, OS); + break; + case Z80::HL: + emitByte(0xe5U, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are BC, DE, HL, IX, IY."); + } + break; + case Z80::RES8bg: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (8U <= static_cast(Operands[0].getImm())) { + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + } + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + switch (Operands[1].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x87U, + CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x80U, + CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x81U, + CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x82U, + CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x83U, + CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x84U, + CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x85U, + CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x84U, + CurByte, OS); // RESn A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x85U, + CurByte, OS); // RESn A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x84U, + CurByte, OS); // RESn A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x85U, + CurByte, OS); // RESn A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::RES8bo: + check_num_operands(MCII.getName(opcode), 3U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (8U <= static_cast(Operands[0].getImm())) { + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + } + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + if (!(Operands[2].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Third operand should be immediate."); + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x86U, + CurByte, OS); + break; + case Z80::RES8bp: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (8U <= static_cast(Operands[0].getImm())) { + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + } + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + switch (Operands[1].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x86U, + CurByte, OS); + break; + case Z80::RET16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xc9U, CurByte, OS); + break; + case Z80::RET16CC: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + if (8U <= static_cast(Operands[0].getImm())) { + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be in range 0..7."); + } + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc0U, + CurByte, OS); + break; + case Z80::RETI16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0x4dU, CurByte, OS); + break; + case Z80::RETN16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0x45U, CurByte, OS); + break; + case Z80::RL8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + emitByte(0x16U, CurByte, OS); + break; + case Z80::RL8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(0x16U, CurByte, OS); + break; + case Z80::RL8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte(0x17U, CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte(0x10U, CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte(0x11U, CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte(0x12U, CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte(0x13U, CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte(0x14U, CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte(0x15U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x14U, CurByte, OS); // RL H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x15U, CurByte, OS); // RL L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x14U, CurByte, OS); // RL H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x15U, CurByte, OS); // RL L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::RLC8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + emitByte(0x06U, CurByte, OS); + break; + case Z80::RLC8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(0x06U, CurByte, OS); + break; + case Z80::RLC8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte(0x07U, CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte(0x01U, CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte(0x02U, CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte(0x03U, CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte(0x04U, CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte(0x05U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x04U, CurByte, OS); // RLC H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x05U, CurByte, OS); // RLC L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x04U, CurByte, OS); // RLC H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x05U, CurByte, OS); // RLC L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::RR8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + emitByte(0x1eU, CurByte, OS); + break; + case Z80::RR8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(0x1eU, CurByte, OS); + break; + case Z80::RR8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte(0x1fU, CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte(0x18U, CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte(0x19U, CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte(0x1aU, CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte(0x1bU, CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte(0x1cU, CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte(0x1dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x1cU, CurByte, OS); // RR H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x1dU, CurByte, OS); // RR L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x1cU, CurByte, OS); // RR H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x1dU, CurByte, OS); // RR L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::RRC8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + emitByte(0x0eU, CurByte, OS); + break; + case Z80::RRC8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(0x0eU, CurByte, OS); + break; + case Z80::RRC8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte(0x0fU, CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte(0x08U, CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte(0x09U, CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte(0x0aU, CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte(0x0bU, CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte(0x0cU, CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte(0x0dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x0cU, CurByte, OS); // RRC H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x0dU, CurByte, OS); // RRC L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x0cU, CurByte, OS); // RRC H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x0dU, CurByte, OS); // RRC L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::SBC16SP: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0x72U, CurByte, OS); + break; + case Z80::SBC16aa: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0x62U, CurByte, OS); + break; + case Z80::SBC16ao: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + emitByte(0xedU, CurByte, OS); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::BC: + emitByte(0x42U, CurByte, OS); + break; + case Z80::DE: + emitByte(0x52U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are BC, DE."); + } + break; + case Z80::SBC8ai: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + emitByte(0xdeU, CurByte, OS); + emitByte(Operands[0].getImm(), CurByte, OS); + break; + case Z80::SBC8ao: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0x9eU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::SBC8ap: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0x9eU, CurByte, OS); + break; + case Z80::SBC8ar: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x9fU, CurByte, OS); + break; + case Z80::B: + emitByte(0x98U, CurByte, OS); + break; + case Z80::C: + emitByte(0x99U, CurByte, OS); + break; + case Z80::D: + emitByte(0x9aU, CurByte, OS); + break; + case Z80::E: + emitByte(0x9bU, CurByte, OS); + break; + case Z80::H: + emitByte(0x9cU, CurByte, OS); + break; + case Z80::L: + emitByte(0x9dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x9cU, CurByte, OS); // SBC A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x9dU, CurByte, OS); // SBC A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x9cU, CurByte, OS); // SBC A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x9dU, CurByte, OS); // SBC A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::SCF: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0x37U, CurByte, OS); + break; + case Z80::SET8bg: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (8U <= static_cast(Operands[0].getImm())) { + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + } + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + switch (Operands[1].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc7U, + CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc0U, + CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc1U, + CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc2U, + CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc3U, + CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc4U, + CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc5U, + CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc4U, + CurByte, OS); // SETn A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc5U, + CurByte, OS); // SETn A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc4U, + CurByte, OS); // SETn A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc5U, + CurByte, OS); // SETn A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::SET8bo: + check_num_operands(MCII.getName(opcode), 3U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (8U <= static_cast(Operands[0].getImm())) { + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + } + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + if (!(Operands[2].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Third operand should be immediate."); + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc6U, + CurByte, OS); + break; + case Z80::SET8bp: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (8U <= static_cast(Operands[0].getImm())) { + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + } + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + switch (Operands[1].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc6U, + CurByte, OS); + break; + case Z80::SLA8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + emitByte(0x26U, CurByte, OS); + break; + case Z80::SLA8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(0x26U, CurByte, OS); + break; + case Z80::SLA8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte(0x27U, CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte(0x20U, CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte(0x21U, CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte(0x22U, CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte(0x23U, CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte(0x24U, CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte(0x25U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x24U, CurByte, OS); // SLA H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x25U, CurByte, OS); // SLA L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x24U, CurByte, OS); // SLA H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x25U, CurByte, OS); // SLA L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::SRA8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + emitByte(0x2eU, CurByte, OS); + break; + case Z80::SRA8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(0x2eU, CurByte, OS); + break; + case Z80::SRA8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte(0x2fU, CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte(0x28U, CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte(0x29U, CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte(0x2aU, CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte(0x2bU, CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte(0x2cU, CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte(0x2dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x2cU, CurByte, OS); // SRA H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x2dU, CurByte, OS); // SRA L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x2cU, CurByte, OS); // SRA H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x2dU, CurByte, OS); // SRA L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::SRL8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + emitByte(0x3eU, CurByte, OS); + break; + case Z80::SRL8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(0x3eU, CurByte, OS); + break; + case Z80::SRL8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte(0x3fU, CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte(0x38U, CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte(0x39U, CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte(0x3aU, CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte(0x3bU, CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte(0x3cU, CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte(0x3dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x3cU, CurByte, OS); // SRL H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x3dU, CurByte, OS); // SRL L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x3cU, CurByte, OS); // SRL H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x3dU, CurByte, OS); // SRL L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::SUB8ai: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + emitByte(0xd6U, CurByte, OS); + emitByte(Operands[0].getImm(), CurByte, OS); + break; + case Z80::SUB8ao: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0x96U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::SUB8ap: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0x96U, CurByte, OS); + break; + case Z80::SUB8ar: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x97U, CurByte, OS); + break; + case Z80::B: + emitByte(0x90U, CurByte, OS); + break; + case Z80::C: + emitByte(0x91U, CurByte, OS); + break; + case Z80::D: + emitByte(0x92U, CurByte, OS); + break; + case Z80::E: + emitByte(0x93U, CurByte, OS); + break; + case Z80::H: + emitByte(0x94U, CurByte, OS); + break; + case Z80::L: + emitByte(0x95U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x94U, CurByte, OS); // SUB A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x95U, CurByte, OS); // SUB A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x94U, CurByte, OS); // SUB A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x95U, CurByte, OS); // SUB A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::XOR8ai: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + emitByte(0xeeU, CurByte, OS); + emitByte(Operands[0].getImm(), CurByte, OS); + break; + case Z80::XOR8ao: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xaeU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::XOR8ap: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xaeU, CurByte, OS); + break; + case Z80::XOR8ar: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xafU, CurByte, OS); + break; + case Z80::B: + emitByte(0xa8U, CurByte, OS); + break; + case Z80::C: + emitByte(0xa9U, CurByte, OS); + break; + case Z80::D: + emitByte(0xaaU, CurByte, OS); + break; + case Z80::E: + emitByte(0xabU, CurByte, OS); + break; + case Z80::H: + emitByte(0xacU, CurByte, OS); + break; + case Z80::L: + emitByte(0xadU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xacU, CurByte, OS); // XOR A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xadU, CurByte, OS); // XOR A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xacU, CurByte, OS); // XOR A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xadU, CurByte, OS); // XOR A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::ADC16SP: + case Z80::ADC16aa: + case Z80::ADC16ao: + case Z80::JP16: + case Z80::JP16CC: + case Z80::JR: + case Z80::JRCC: + case Z80::LD16or: + case Z80::LD16pr: + case Z80::LD16ro: + case Z80::LD16rp: + report_fatal_instr_problem(MCII.getName(opcode), "Not implemented."); + break; + default: + std::string msg; + raw_string_ostream Msg(msg); + Msg << "Not supported instr: " << MCII.getName(opcode) << ' ' << MI; + report_fatal_error(Msg.str()); + } +} + +} // end of namespace llvm diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCCodeEmitter.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCCodeEmitter.h new file mode 100644 index 00000000000000..c2155d465b0442 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCCodeEmitter.h @@ -0,0 +1,57 @@ +//===-- Z80MCCodeEmitter.h - Convert Z80 Code to Machine Code -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the Z80MCCodeEmitter class. +// +//===----------------------------------------------------------------------===// +// + +#ifndef LLVM_Z80_CODE_EMITTER_H +#define LLVM_Z80_CODE_EMITTER_H + +#include "Z80FixupKinds.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/Support/DataTypes.h" + +#define GET_INSTRINFO_OPERAND_TYPES_ENUM +#include "Z80GenInstrInfo.inc" + +#include + +namespace llvm { + +class MCContext; +class MCFixup; +class MCInst; +class MCInstrInfo; +class MCSubtargetInfo; +class raw_ostream; + +/// Writes Z80 machine code to a stream. +class Z80MCCodeEmitter : public MCCodeEmitter { +public: + Z80MCCodeEmitter(const MCInstrInfo &MCII, MCContext &Ctx) : MCII(MCII) {} + +private: + void emitByte(uint8_t C, unsigned &CurByte, raw_ostream &OS) const; + + void encodeInstruction(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const override; + + Z80MCCodeEmitter(const Z80MCCodeEmitter &) = delete; + void operator=(const Z80MCCodeEmitter &) = delete; + + const MCInstrInfo &MCII; +}; + +} // namespace llvm + +#endif // LLVM_Z80_CODE_EMITTER_H diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.cpp index b31616295649ce..a39ac387db1715 100644 --- a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.cpp +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.cpp @@ -13,13 +13,28 @@ #include "Z80MCTargetDesc.h" #include "EZ80InstPrinter.h" +#include "Z80AsmBackend.h" +#include "Z80ELFStreamer.h" #include "Z80InstPrinter.h" #include "Z80MCAsmInfo.h" +#include "Z80MCCodeEmitter.h" #include "Z80TargetStreamer.h" +#include "llvm/ADT/Triple.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstPrinter.h" #include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptions.h" #include "llvm/Support/TargetRegistry.h" + +#include +#include + using namespace llvm; #define GET_REGINFO_MC_DESC @@ -60,6 +75,20 @@ static MCAsmInfo *createZ80MCAsmInfo(const MCRegisterInfo &MRI, return new Z80MCAsmInfo(TheTriple); } +MCInstrInfo *createZ80MCInstrInfo() { + MCInstrInfo *X = new MCInstrInfo(); + InitZ80MCInstrInfo(X); + + return X; +} + +static MCRegisterInfo *createZ80MCRegisterInfo(const Triple &TT) { + MCRegisterInfo *X = new MCRegisterInfo(); + InitZ80MCRegisterInfo(X, 0); + + return X; +} + static MCInstPrinter *createZ80MCInstPrinter(const Triple &T, unsigned SyntaxVariant, const MCAsmInfo &MAI, @@ -74,21 +103,64 @@ static MCInstPrinter *createZ80MCInstPrinter(const Triple &T, static MCTargetStreamer * createZ80AsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS, - MCInstPrinter */*InstPrint*/, + MCInstPrinter * /*InstPrint*/, bool /*isVerboseAsm*/) { return new Z80TargetAsmStreamer(S, OS); } +static MCCodeEmitter *createZ80MCCodeEmitter(const MCInstrInfo &MCII, + const MCRegisterInfo &MRI, + MCContext &Ctx) { + return new Z80MCCodeEmitter(MCII, Ctx); +} + +static MCStreamer *createMCStreamer(const Triple &T, MCContext &Context, + std::unique_ptr &&MAB, + std::unique_ptr &&OW, + std::unique_ptr &&Emitter, + bool RelaxAll) { + return createELFStreamer(Context, std::move(MAB), std::move(OW), + std::move(Emitter), RelaxAll); +} + +static MCTargetStreamer * +createZ80ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { + return new Z80ELFStreamer(S, STI); +} + // Force static initialization. extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeZ80TargetMC() { for (Target *T : {&getTheZ80Target(), &getTheEZ80Target()}) { // Register the MC asm info. RegisterMCAsmInfoFn X(*T, createZ80MCAsmInfo); + // Register the MC instruction info. + TargetRegistry::RegisterMCInstrInfo(*T, createZ80MCInstrInfo); + + // Register the MC register info. + TargetRegistry::RegisterMCRegInfo(*T, createZ80MCRegisterInfo); + + // Register the MC subtarget info. + TargetRegistry::RegisterMCSubtargetInfo(*T, + Z80_MC::createZ80MCSubtargetInfo); + // Register the MCInstPrinter. TargetRegistry::RegisterMCInstPrinter(*T, createZ80MCInstPrinter); // Register the asm target streamer. TargetRegistry::RegisterAsmTargetStreamer(*T, createZ80AsmTargetStreamer); } + // Register the MC Code Emitter. + TargetRegistry::RegisterMCCodeEmitter(getTheZ80Target(), + createZ80MCCodeEmitter); + + // Register the obj streamer. + TargetRegistry::RegisterELFStreamer(getTheZ80Target(), createMCStreamer); + + // Register the obj target streamer. + TargetRegistry::RegisterObjectTargetStreamer(getTheZ80Target(), + createZ80ObjectTargetStreamer); + + // Register the asm backend (as little endian). + TargetRegistry::RegisterMCAsmBackend(getTheZ80Target(), createZ80AsmBackend); } diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.h index 6beaaf7f96b522..2a425ace7d8bed 100644 --- a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.h +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.h @@ -15,6 +15,7 @@ #define LLVM_LIB_TARGET_Z80_MCTARGETDESC_Z80MCTARGETDESC_H #include "llvm/Support/DataTypes.h" +#include #include #include @@ -23,6 +24,7 @@ class MCAsmBackend; class MCCodeEmitter; class MCContext; class MCInstrInfo; +class MCObjectTargetWriter; class MCObjectWriter; class MCRegisterInfo; class MCSubtargetInfo; @@ -42,15 +44,12 @@ std::string ParseZ80Triple(const Triple &TT); /// do not need to go through TargetRegistry. MCSubtargetInfo *createZ80MCSubtargetInfo(const Triple &TT, StringRef CPU, StringRef FS); -} +} // namespace Z80_MC -MCCodeEmitter *createZ80MCCodeEmitter(const MCInstrInfo &MCII, - const MCRegisterInfo &MRI, - MCContext &Ctx); +MCAsmBackend *createZ80AsmBackend(const Target &T, const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const llvm::MCTargetOptions &TO); -MCAsmBackend *createZ80AsmBackend(const Target &T, const MCRegisterInfo &MRI, - const Triple &TT, StringRef CPU, - const MCTargetOptions &Options); MCAsmBackend *createEZ80AsmBackend(const Target &T, const MCRegisterInfo &MRI, const Triple &TT, StringRef CPU, const MCTargetOptions &Options); @@ -59,10 +58,9 @@ MCAsmBackend *createEZ80AsmBackend(const Target &T, const MCRegisterInfo &MRI, std::unique_ptr createZ80OMFObjectWriter(raw_pwrite_stream &OS); /// Construct a Z80 ELF object writer. -std::unique_ptr createZ80ELFObjectWriter(raw_pwrite_stream &OS, - uint8_t OSABI = 0); +std::unique_ptr createZ80ELFObjectWriter(uint8_t OSABI); -} // End llvm namespace +} // namespace llvm // Defines symbolic names for Z80 registers. This defines a mapping from // register name to register number.