//===-- M68kInstrControl.td - Control Flow Instructions ----*- 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 // //===----------------------------------------------------------------------===// /// /// \file /// This file describes the M68k jump, return, call, and related instructions. /// Here is the current status of the file: /// /// Machine: /// /// BRA [x] BSR [ ] Bcc [~] DBcc [ ] FBcc [ ] /// FDBcc [ ] FNOP [ ] FPn [ ] FScc [ ] FTST [ ] /// JMP [~] JSR [x] NOP [x] RTD [!] RTR [ ] /// RTS [x] Scc [~] TST [ ] /// /// Pseudo: /// /// RET [x] /// TCRETURNj [x] TCRETURNq [x] /// TAILJMPj [x] TAILJMPq [x] /// /// Map: /// /// [ ] - was not touched at all /// [!] - requires extarnal stuff implemented /// [~] - in progress but usable /// [x] - done /// /// /// NOTE /// Though branch and jump instructions are using memory operands they /// DO NOT read the jump address from memory, they just calculate EA /// and jump there. /// //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // NOP //===----------------------------------------------------------------------===// let hasSideEffects = 0 in { def NOP : MxInst<(outs), (ins), "nop", []> { let Inst = (descend 0b0100, 0b1110, 0b0111, 0b0001); } } //===----------------------------------------------------------------------===// // Conditions //===----------------------------------------------------------------------===// /// CC—Carry clear GE—Greater than or equal /// LS—Lower or same PL—Plus /// CS—Carry set GT—Greater than /// LT—Less than T—Always true* /// EQ—Equal HI—Higher /// MI—Minus VC—Overflow clear /// F—Never true* LE—Less than or equal /// NE—Not equal VS—Overflow set /// /// *Not applicable to the Bcc instructions. class MxEncCondOp cond> { dag Value = (descend cond); } def MxCCt : MxEncCondOp<0b0000>; def MxCCf : MxEncCondOp<0b0001>; def MxCChi : MxEncCondOp<0b0010>; def MxCCls : MxEncCondOp<0b0011>; def MxCCcc : MxEncCondOp<0b0100>; def MxCCcs : MxEncCondOp<0b0101>; def MxCCne : MxEncCondOp<0b0110>; def MxCCeq : MxEncCondOp<0b0111>; def MxCCvc : MxEncCondOp<0b1000>; def MxCCvs : MxEncCondOp<0b1001>; def MxCCpl : MxEncCondOp<0b1010>; def MxCCmi : MxEncCondOp<0b1011>; def MxCCge : MxEncCondOp<0b1100>; def MxCClt : MxEncCondOp<0b1101>; def MxCCgt : MxEncCondOp<0b1110>; def MxCCle : MxEncCondOp<0b1111>; /// --------------------------------+---------+--------- /// F E D C | B A 9 8 | 7 6 | 5 4 3 | 2 1 0 /// --------------------------------+---------+--------- /// 0 1 0 1 | CONDITION | 1 1 | MODE | REG /// ---------------------------------------------------- let Uses = [CCR] in { class MxSccR : MxInst<(outs MxDRD8:$dst), (ins), "s"#CC#"\t$dst", [(set i8:$dst, (MxSetCC !cast("MxCOND"#CC), CCR))]> { let Inst = (descend 0b0101, !cast("MxCC"#CC).Value, 0b11, /*MODE without last bit*/0b00, /*REGISTER prefixed with D/A bit*/(operand "$dst", 4)); } class MxSccM : MxInst<(outs), (ins MEMOpd:$dst), "s"#CC#"\t$dst", [(store (MxSetCC !cast("MxCOND"#CC), CCR), MEMPat:$dst)]> { let Inst = (ascend (descend 0b0101, !cast("MxCC"#CC).Value, 0b11, DST_ENC.EA), DST_ENC.Supplement ); } } foreach cc = [ "cc", "ls", "lt", "eq", "mi", "f", "ne", "ge", "cs", "pl", "gt", "t", "hi", "vc", "le", "vs"] in { def SET#"d8"#cc : MxSccR; def SET#"j8"#cc : MxSccM>; def SET#"p8"#cc : MxSccM>; } //===----------------------------------------------------------------------===// // Jumps //===----------------------------------------------------------------------===// ///------------------------------+---------+--------- /// F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 ///------------------------------+---------+--------- /// 0 1 0 0 1 1 1 0 1 1 | MODE | REG ///------------------------------+---------+--------- let isBranch = 1, isTerminator = 1, isBarrier = 1, isIndirectBranch = 1 in class MxJMP : MxInst<(outs), (ins LOCOp:$dst), "jmp\t$dst", [(brind iPTR:$dst)]> { let Inst = (ascend (descend 0b0100, 0b1110, 0b11, DST_ENC.EA), DST_ENC.Supplement ); } def JMP32j : MxJMP>; // FIXME Support 16 bit indirect jump. // Currently M68k does not allow 16 bit indirect jumps use sext operands // def JMP16r : MxInst<(outs), (ins M68k_ARI16:$dst), // "jmp\t$dst", // [(brind AR16:$dst)]>; //===----------------------------------------------------------------------===// // Branches //===----------------------------------------------------------------------===// /// -------------------------------------------------- /// F E D C | B A 9 8 | 7 6 5 4 3 2 1 0 /// -------------------------------------------------- /// 0 1 1 0 | CONDITION | 8-BIT DISPLACEMENT /// -------------------------------------------------- /// 16-BIT DISPLACEMENT IF 8-BIT DISPLACEMENT = $00 /// -------------------------------------------------- /// 32-BIT DISPLACEMENT IF 8-BIT DISPLACEMENT = $FF /// -------------------------------------------------- let isBranch = 1, isTerminator = 1, Uses = [CCR] in class MxBcc : MxInst<(outs), (ins TARGET:$dst), "b"#cc#"\t$dst", []> { // FIXME: If we want to avoid supplying disp_16_32 with empty // (ascend) for 16/32 bits variants, we can use conditional // bang operator like this: // ``` // class MxBcc // ... // let Inst = !cond( // !eq(SIZE, 8): /* encoding for Bcc8 */ // !eq(SIZE, 16): /* encoding for Bcc16 */ // !eq(SIZE, 32): /* encoding for Bcc32 */ // ); let Inst = (ascend (descend 0b0110, !cast("MxCC"#cc).Value, disp_8), disp_16_32 ); } foreach cc = [ "cc", "ls", "lt", "eq", "mi", "ne", "ge", "cs", "pl", "gt", "hi", "vc", "le", "vs"] in { def B#cc#"8" : MxBcc")), (ascend)>; def B#cc#"16" : MxBcc"))>; } foreach cc = [ "cc", "ls", "lt", "eq", "mi", "ne", "ge", "cs", "pl", "gt", "hi", "vc", "le", "vs"] in { def : Pat<(MxBrCond bb:$target, !cast("MxCOND"#cc), CCR), (!cast("B"#cc#"8") MxBrTarget8:$target)>; } /// ------------------------------------------------- /// F E D C B A 9 8 | 7 6 5 4 3 2 1 0 /// ------------------------------------------------- /// 0 1 1 0 0 0 0 0 | 8-BIT DISPLACEMENT /// ------------------------------------------------- /// 16-BIT DISPLACEMENT IF 8-BIT DISPLACEMENT = $00 /// ------------------------------------------------- /// 32-BIT DISPLACEMENT IF 8-BIT DISPLACEMENT = $FF /// ------------------------------------------------- let isBranch = 1, isTerminator = 1, isBarrier = 1 in class MxBra : MxInst<(outs), (ins TARGET:$dst), "bra\t$dst", []> { let Inst = (ascend (descend 0b0110, 0b0000, disp_8), disp_16_32 ); } def BRA8 : MxBra")), (ascend)>; def BRA16 : MxBra"))>; def : Pat<(br bb:$target), (BRA8 MxBrTarget8:$target)>; //===----------------------------------------------------------------------===// // Call //===----------------------------------------------------------------------===// // 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 Uses = [SP] in let isCall = 1 in ///------------------------------+---------+--------- /// F E D C B A 9 8 7 6 | 5 4 3 | 2 1 0 ///------------------------------+---------+--------- /// 0 1 0 0 1 1 1 0 1 0 | MODE | REG ///------------------------------+---------+--------- class MxCall : MxInst<(outs), (ins LOCOp:$dst), "jsr\t$dst", []> { let Inst = (ascend (descend 0b0100, 0b1110, 0b10, DST_ENC.EA), DST_ENC.Supplement ); } def CALLk : MxCall>; def CALLq : MxCall>; def CALLb : MxCall>; def CALLj : MxCall>; multiclass CallPat { let Predicates = [pred] in { def : Pat<(MxCall (i32 tglobaladdr:$dst)), (callOp tglobaladdr:$dst)>; def : Pat<(MxCall (i32 texternalsym:$dst)), (callOp texternalsym:$dst)>; def : Pat<(MxCall (i32 imm:$dst)), (callOp imm:$dst)>; } } defm : CallPat; defm : CallPat; def : Pat<(MxCall iPTR:$dst), (CALLj MxARI32:$dst)>; //===----------------------------------------------------------------------===// // Tail Call //===----------------------------------------------------------------------===// let isCodeGenOnly = 1 in { let Uses = [SP] in { let isCall = 1, isTerminator = 1, isBarrier = 1 in { let isReturn = 1 in def TCRETURNq : MxPseudo<(outs), (ins MxPCD32:$dst, i32imm:$adj)>; def TAILJMPq : MxPseudo<(outs), (ins MxPCD32:$dst)>; // NOTE j does not mean load and jump M68k jmp just calculates EA and jumps // and it is using Mem form like (An) thus j letter. let isReturn = 1 in def TCRETURNj : MxPseudo<(outs), (ins MxARI32_TC:$dst, i32imm:$adj)>; def TAILJMPj : MxPseudo<(outs), (ins MxARI32_TC:$dst)>; } // isCall = 1, isTerminator = 1, isBarrier = 1 } // Uses = [SP] } // isCodeGenOnly = 1 //===----------------------------------------------------------------------===// // Return //===----------------------------------------------------------------------===// // TODO Implement LINK/UNLK let isTerminator = 1, isReturn = 1, isBarrier = 1, hasCtrlDep = 1 in { def RTS : MxInst<(outs), (ins), "rts", []> { let Inst = (descend 0b0100, 0b1110, 0b0111, 0b0101); } let isCodeGenOnly = 1 in def RET : MxPseudo<(outs), (ins i32imm:$adj, variable_ops), [(MxRet timm:$adj)]>; } // isTerminator = 1, isReturn = 1, isBarrier = 1, hasCtrlDep = 1 //===----------------------------------------------------------------------===// // SETCC_C Patterns //===----------------------------------------------------------------------===// // Use subx to materialize carry bit. let Uses = [CCR], Defs = [CCR], isPseudo = 1 in { // FIXME These are pseudo ops that should be replaced with Pat<> patterns. // However, Pat<> can't replicate the destination reg into the inputs of the // result. def SETCS_C8d : MxPseudo<(outs MxDRD8:$dst), (ins), [(set MxDRD8:$dst, (MxSetCC_C MxCONDcs, CCR))]>; def SETCS_C16d : MxPseudo<(outs MxDRD16:$dst), (ins), [(set MxDRD16:$dst, (MxSetCC_C MxCONDcs, CCR))]>; def SETCS_C32d : MxPseudo<(outs MxXRD32:$dst), (ins), [(set MxXRD32:$dst, (MxSetCC_C MxCONDcs, CCR))]>; } // Uses = [CCR], Defs = [CCR], isPseudo = 1 def : Pat<(i16 (anyext (i8 (MxSetCC_C MxCONDcs, CCR)))), (SETCS_C16d)>; def : Pat<(i32 (anyext (i8 (MxSetCC_C MxCONDcs, CCR)))), (SETCS_C32d)>; def : Pat<(i16 (sext (i8 (MxSetCC_C MxCONDcs, CCR)))), (SETCS_C16d)>; def : Pat<(i32 (sext (i8 (MxSetCC_C MxCONDcs, CCR)))), (SETCS_C32d)>; // We canonicalize 'scs' to "(and (subx reg,reg), 1)" on the hope that the and // will be eliminated and that the subx can be extended up to a wider type. When // this happens, it is great. However, if we are left with an 8-bit subx and an // and, we might as well just match it as a setb. def : Pat<(and (i8 (MxSetCC_C MxCONDcs, CCR)), 1), (SETd8cs)>; // (add OP, SETB) -> (addx OP, (move 0)) def : Pat<(add (and (i8 (MxSetCC_C MxCONDcs, CCR)), 1), MxDRD8:$op), (ADDX8dd MxDRD8:$op, (MOV8di 0))>; def : Pat<(add (and (i32 (MxSetCC_C MxCONDcs, CCR)), 1), MxXRD32:$op), (ADDX32dd MxDRD32:$op, (MOV32ri 0))>; // (sub OP, SETB) -> (subx OP, (move 0)) def : Pat<(sub MxDRD8:$op, (and (i8 (MxSetCC_C MxCONDcs, CCR)), 1)), (SUBX8dd MxDRD8:$op, (MOV8di 0))>; def : Pat<(sub MxXRD32:$op, (and (i32 (MxSetCC_C MxCONDcs, CCR)), 1)), (SUBX32dd MxDRD32:$op, (MOV32ri 0))>; // (sub OP, SETCC_CARRY) -> (addx OP, (move 0)) def : Pat<(sub MxDRD8:$op, (i8 (MxSetCC_C MxCONDcs, CCR))), (ADDX8dd MxDRD8:$op, (MOV8di 0))>; def : Pat<(sub MxXRD32:$op, (i32 (MxSetCC_C MxCONDcs, CCR))), (ADDX32dd MxDRD32:$op, (MOV32ri 0))>;