forked from OSchip/llvm-project
356 lines
13 KiB
TableGen
356 lines
13 KiB
TableGen
//===-- 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<bits<4> 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<string CC>
|
|
: MxInst<(outs MxDRD8:$dst), (ins), "s"#CC#"\t$dst",
|
|
[(set i8:$dst, (MxSetCC !cast<PatLeaf>("MxCOND"#CC), CCR))]> {
|
|
let Inst = (descend 0b0101, !cast<MxEncCondOp>("MxCC"#CC).Value, 0b11,
|
|
/*MODE without last bit*/0b00,
|
|
/*REGISTER prefixed with D/A bit*/(operand "$dst", 4));
|
|
}
|
|
|
|
class MxSccM<string CC, MxOperand MEMOpd, ComplexPattern MEMPat, MxEncMemOp DST_ENC>
|
|
: MxInst<(outs), (ins MEMOpd:$dst), "s"#CC#"\t$dst",
|
|
[(store (MxSetCC !cast<PatLeaf>("MxCOND"#CC), CCR), MEMPat:$dst)]> {
|
|
let Inst =
|
|
(ascend
|
|
(descend 0b0101, !cast<MxEncCondOp>("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<cc>;
|
|
def SET#"j8"#cc : MxSccM<cc, MxType8.JOp, MxType8.JPat, MxEncAddrMode_j<"dst">>;
|
|
def SET#"p8"#cc : MxSccM<cc, MxType8.POp, MxType8.PPat, MxEncAddrMode_p<"dst">>;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 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<MxOperand LOCOp, MxEncMemOp DST_ENC>
|
|
: 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<MxARI32, MxEncAddrMode_j<"dst">>;
|
|
|
|
|
|
// 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<string cc, Operand TARGET, dag disp_8, dag disp_16_32>
|
|
: 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<string cc, Operand TARGET, int SIZE>
|
|
// ...
|
|
// 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<MxEncCondOp>("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<cc, MxBrTarget8,
|
|
(operand "$dst", 8, (encoder "encodePCRelImm<8>")), (ascend)>;
|
|
|
|
def B#cc#"16"
|
|
: MxBcc<cc, MxBrTarget16, (descend 0b0000, 0b0000),
|
|
(operand "$dst", 16, (encoder "encodePCRelImm<16>"))>;
|
|
}
|
|
|
|
foreach cc = [ "cc", "ls", "lt", "eq", "mi", "ne", "ge",
|
|
"cs", "pl", "gt", "hi", "vc", "le", "vs"] in {
|
|
def : Pat<(MxBrCond bb:$target, !cast<PatLeaf>("MxCOND"#cc), CCR),
|
|
(!cast<Instruction>("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<Operand TARGET, dag disp_8, dag disp_16_32>
|
|
: MxInst<(outs), (ins TARGET:$dst), "bra\t$dst", []> {
|
|
let Inst =
|
|
(ascend
|
|
(descend 0b0110, 0b0000, disp_8),
|
|
disp_16_32
|
|
);
|
|
}
|
|
|
|
def BRA8 : MxBra<MxBrTarget8,
|
|
(operand "$dst", 8, (encoder "encodePCRelImm<8>")), (ascend)>;
|
|
|
|
def BRA16 : MxBra<MxBrTarget16, (descend 0b0000, 0b0000),
|
|
(operand "$dst", 16, (encoder "encodePCRelImm<16>"))>;
|
|
|
|
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<MxOperand LOCOp, MxEncMemOp DST_ENC>
|
|
: MxInst<(outs), (ins LOCOp:$dst), "jsr\t$dst", []> {
|
|
let Inst =
|
|
(ascend
|
|
(descend 0b0100, 0b1110, 0b10, DST_ENC.EA),
|
|
DST_ENC.Supplement
|
|
);
|
|
}
|
|
|
|
def CALLk : MxCall<MxPCI32, MxEncAddrMode_k<"dst">>;
|
|
def CALLq : MxCall<MxPCD32, MxEncAddrMode_q<"dst">>;
|
|
def CALLb : MxCall<MxAL32, MxEncAddrMode_abs<"dst", true>>;
|
|
def CALLj : MxCall<MxARI32, MxEncAddrMode_j<"dst">>;
|
|
|
|
multiclass CallPat<MxCall callOp, Predicate pred> {
|
|
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<CALLq, IsPIC>;
|
|
defm : CallPat<CALLb, IsNotPIC>;
|
|
|
|
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))>;
|