1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 10:42:39 +01:00

[Sparc] Fixes for the internal assembler

* Prevent the generation of invalid shift instructions by constraining
  the immediate field. I've limited the shift field to constant values
  only, adding the `R_SPARC_5`/`R_SPARC_6` relocations is trivial if
  needed (but I can't really think of a use case for those).
* Fix the generation of PC-relative `call`
* Fix the transformation of `jmp sym` into `jmpl`
* Emit fixups for simm13 operands

I moved the choice of the correct relocation into the code emitter as I've
seen the other backends do, it can be definitely cleaner but the aim was
to reduce the scope of the patch as much as possible.

Fixes the problems raised by joerg in L254199

Reviewed By: dcederman

Differential Revision: https://reviews.llvm.org/D78193
This commit is contained in:
LemonBoy 2021-01-04 09:41:58 +01:00 committed by Daniel Cederman
parent 1f73b986d5
commit 42b48dbe04
13 changed files with 251 additions and 58 deletions

View File

@ -82,6 +82,11 @@ class SparcAsmParser : public MCTargetAsmParser {
OperandMatchResultTy parseMembarTag(OperandVector &Operands);
template <unsigned N>
OperandMatchResultTy parseShiftAmtImm(OperandVector &Operands);
OperandMatchResultTy parseCallTarget(OperandVector &Operands);
OperandMatchResultTy parseOperand(OperandVector &Operands, StringRef Name);
OperandMatchResultTy
@ -262,6 +267,36 @@ public:
bool isMEMri() const { return Kind == k_MemoryImm; }
bool isMembarTag() const { return Kind == k_Immediate; }
bool isCallTarget() const {
if (!isImm())
return false;
if (const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Imm.Val))
return CE->getValue() % 4 == 0;
return true;
}
bool isShiftAmtImm5() const {
if (!isImm())
return false;
if (const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Imm.Val))
return isUInt<5>(CE->getValue());
return false;
}
bool isShiftAmtImm6() const {
if (!isImm())
return false;
if (const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Imm.Val))
return isUInt<6>(CE->getValue());
return false;
}
bool isIntReg() const {
return (Kind == k_Register && Reg.Kind == rk_IntReg);
}
@ -343,6 +378,15 @@ public:
addExpr(Inst, Expr);
}
void addShiftAmtImm5Operands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
addExpr(Inst, getImm());
}
void addShiftAmtImm6Operands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
addExpr(Inst, getImm());
}
void addExpr(MCInst &Inst, const MCExpr *Expr) const{
// Add as immediate when possible. Null MCExpr = 0.
if (!Expr)
@ -377,6 +421,11 @@ public:
addExpr(Inst, Expr);
}
void addCallTargetOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
addExpr(Inst, getImm());
}
static std::unique_ptr<SparcOperand> CreateToken(StringRef Str, SMLoc S) {
auto Op = std::make_unique<SparcOperand>(k_Token);
Op->Tok.Data = Str.data();
@ -645,7 +694,7 @@ OperandMatchResultTy SparcAsmParser::tryParseRegister(unsigned &RegNo,
EndLoc = Tok.getEndLoc();
RegNo = 0;
if (getLexer().getKind() != AsmToken::Percent)
return MatchOperand_Success;
return MatchOperand_NoMatch;
Parser.Lex();
unsigned regKind = SparcOperand::rk_None;
if (matchRegisterName(Tok, RegNo, regKind)) {
@ -729,37 +778,74 @@ ParseDirective(AsmToken DirectiveID)
OperandMatchResultTy
SparcAsmParser::parseMEMOperand(OperandVector &Operands) {
SMLoc S, E;
unsigned BaseReg = 0;
if (ParseRegister(BaseReg, S, E)) {
std::unique_ptr<SparcOperand> LHS;
if (parseSparcAsmOperand(LHS) != MatchOperand_Success)
return MatchOperand_NoMatch;
}
switch (getLexer().getKind()) {
default: return MatchOperand_NoMatch;
case AsmToken::Comma:
case AsmToken::RBrac:
case AsmToken::EndOfStatement:
Operands.push_back(SparcOperand::CreateMEMr(BaseReg, S, E));
// Single immediate operand
if (LHS->isImm()) {
Operands.push_back(SparcOperand::MorphToMEMri(Sparc::G0, std::move(LHS)));
return MatchOperand_Success;
case AsmToken:: Plus:
Parser.Lex(); // Eat the '+'
break;
case AsmToken::Minus:
break;
}
std::unique_ptr<SparcOperand> Offset;
OperandMatchResultTy ResTy = parseSparcAsmOperand(Offset);
if (ResTy != MatchOperand_Success || !Offset)
if (!LHS->isIntReg()) {
Error(LHS->getStartLoc(), "invalid register kind for this operand");
return MatchOperand_ParseFail;
}
AsmToken Tok = getLexer().getTok();
// The plus token may be followed by a register or an immediate value, the
// minus one is always interpreted as sign for the immediate value
if (Tok.is(AsmToken::Plus) || Tok.is(AsmToken::Minus)) {
(void)Parser.parseOptionalToken(AsmToken::Plus);
std::unique_ptr<SparcOperand> RHS;
if (parseSparcAsmOperand(RHS) != MatchOperand_Success)
return MatchOperand_NoMatch;
if (RHS->isReg() && !RHS->isIntReg()) {
Error(RHS->getStartLoc(), "invalid register kind for this operand");
return MatchOperand_ParseFail;
}
Operands.push_back(
RHS->isImm()
? SparcOperand::MorphToMEMri(LHS->getReg(), std::move(RHS))
: SparcOperand::MorphToMEMrr(LHS->getReg(), std::move(RHS)));
return MatchOperand_Success;
}
Operands.push_back(SparcOperand::CreateMEMr(LHS->getReg(), S, E));
return MatchOperand_Success;
}
template <unsigned N>
OperandMatchResultTy SparcAsmParser::parseShiftAmtImm(OperandVector &Operands) {
SMLoc S = Parser.getTok().getLoc();
SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1);
// This is a register, not an immediate
if (getLexer().getKind() == AsmToken::Percent)
return MatchOperand_NoMatch;
Operands.push_back(
Offset->isImm() ? SparcOperand::MorphToMEMri(BaseReg, std::move(Offset))
: SparcOperand::MorphToMEMrr(BaseReg, std::move(Offset)));
const MCExpr *Expr;
if (getParser().parseExpression(Expr))
return MatchOperand_ParseFail;
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Expr);
if (!CE) {
Error(S, "constant expression expected");
return MatchOperand_ParseFail;
}
if (!isUInt<N>(CE->getValue())) {
Error(S, "immediate shift value out of range");
return MatchOperand_ParseFail;
}
Operands.push_back(SparcOperand::CreateImm(Expr, S, E));
return MatchOperand_Success;
}
@ -809,6 +895,33 @@ OperandMatchResultTy SparcAsmParser::parseMembarTag(OperandVector &Operands) {
return MatchOperand_Success;
}
OperandMatchResultTy SparcAsmParser::parseCallTarget(OperandVector &Operands) {
SMLoc S = Parser.getTok().getLoc();
SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1);
switch (getLexer().getKind()) {
default:
return MatchOperand_NoMatch;
case AsmToken::LParen:
case AsmToken::Integer:
case AsmToken::Identifier:
case AsmToken::Dot:
break;
}
const MCExpr *DestValue;
if (getParser().parseExpression(DestValue))
return MatchOperand_NoMatch;
bool IsPic = getContext().getObjectFileInfo()->isPositionIndependent();
SparcMCExpr::VariantKind Kind =
IsPic ? SparcMCExpr::VK_Sparc_WPLT30 : SparcMCExpr::VK_Sparc_WDISP30;
const MCExpr *DestExpr = SparcMCExpr::create(Kind, DestValue, getContext());
Operands.push_back(SparcOperand::CreateImm(DestExpr, S, E));
return MatchOperand_Success;
}
OperandMatchResultTy
SparcAsmParser::parseOperand(OperandVector &Operands, StringRef Mnemonic) {
@ -936,6 +1049,7 @@ SparcAsmParser::parseSparcAsmOperand(std::unique_ptr<SparcOperand> &Op,
}
break;
case AsmToken::Plus:
case AsmToken::Minus:
case AsmToken::Integer:
case AsmToken::LParen:
@ -1272,7 +1386,7 @@ const SparcMCExpr *
SparcAsmParser::adjustPICRelocation(SparcMCExpr::VariantKind VK,
const MCExpr *subExpr) {
// When in PIC mode, "%lo(...)" and "%hi(...)" behave differently.
// If the expression refers contains _GLOBAL_OFFSETE_TABLE, it is
// If the expression refers contains _GLOBAL_OFFSET_TABLE, it is
// actually a %pc10 or %pc22 relocation. Otherwise, they are interpreted
// as %got10 or %got22 relocation.

View File

@ -141,24 +141,34 @@ void SparcInstPrinter::printOperand(const MCInst *MI, int opNum,
void SparcInstPrinter::printMemOperand(const MCInst *MI, int opNum,
const MCSubtargetInfo &STI,
raw_ostream &O, const char *Modifier) {
printOperand(MI, opNum, STI, O);
// If this is an ADD operand, emit it like normal operands.
if (Modifier && !strcmp(Modifier, "arith")) {
printOperand(MI, opNum, STI, O);
O << ", ";
printOperand(MI, opNum+1, STI, O);
printOperand(MI, opNum + 1, STI, O);
return;
}
const MCOperand &MO = MI->getOperand(opNum+1);
if (MO.isReg() && MO.getReg() == SP::G0)
return; // don't print "+%g0"
if (MO.isImm() && MO.getImm() == 0)
return; // don't print "+0"
const MCOperand &Op1 = MI->getOperand(opNum);
const MCOperand &Op2 = MI->getOperand(opNum + 1);
O << "+";
bool PrintedFirstOperand = false;
if (Op1.isReg() && Op1.getReg() != SP::G0) {
printOperand(MI, opNum, STI, O);
PrintedFirstOperand = true;
}
printOperand(MI, opNum+1, STI, O);
// Skip the second operand iff it adds nothing (literal 0 or %g0) and we've
// already printed the first one
const bool SkipSecondOperand =
PrintedFirstOperand && ((Op2.isReg() && Op2.getReg() == SP::G0) ||
(Op2.isImm() && Op2.getImm() == 0));
if (!SkipSecondOperand) {
if (PrintedFirstOperand)
O << '+';
printOperand(MI, opNum + 1, STI, O);
}
}
void SparcInstPrinter::printCCOperand(const MCInst *MI, int opNum,

View File

@ -22,6 +22,7 @@
#include "llvm/MC/MCFixup.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCSymbol.h"
@ -68,13 +69,15 @@ public:
unsigned getMachineOpValue(const MCInst &MI, const MCOperand &MO,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;
unsigned getCallTargetOpValue(const MCInst &MI, unsigned OpNo,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;
unsigned getBranchTargetOpValue(const MCInst &MI, unsigned OpNo,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;
unsigned getSImm13OpValue(const MCInst &MI, unsigned OpNo,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;
unsigned getBranchPredTargetOpValue(const MCInst &MI, unsigned OpNo,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;
@ -146,20 +149,50 @@ getMachineOpValue(const MCInst &MI, const MCOperand &MO,
return 0;
}
unsigned
SparcMCCodeEmitter::getSImm13OpValue(const MCInst &MI, unsigned OpNo,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const {
const MCOperand &MO = MI.getOperand(OpNo);
if (MO.isImm())
return MO.getImm();
assert(MO.isExpr() &&
"getSImm13OpValue expects only expressions or an immediate");
const MCExpr *Expr = MO.getExpr();
// Constant value, no fixup is needed
if (const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Expr))
return CE->getValue();
MCFixupKind Kind;
if (const SparcMCExpr *SExpr = dyn_cast<SparcMCExpr>(Expr)) {
Kind = MCFixupKind(SExpr->getFixupKind());
} else {
bool IsPic = Ctx.getObjectFileInfo()->isPositionIndependent();
Kind = IsPic ? MCFixupKind(Sparc::fixup_sparc_got13)
: MCFixupKind(Sparc::fixup_sparc_13);
}
Fixups.push_back(MCFixup::create(0, Expr, Kind));
return 0;
}
unsigned SparcMCCodeEmitter::
getCallTargetOpValue(const MCInst &MI, unsigned OpNo,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const {
const MCOperand &MO = MI.getOperand(OpNo);
if (MO.isReg() || MO.isImm())
return getMachineOpValue(MI, MO, Fixups, STI);
const MCExpr *Expr = MO.getExpr();
const SparcMCExpr *SExpr = dyn_cast<SparcMCExpr>(Expr);
if (MI.getOpcode() == SP::TLS_CALL) {
// No fixups for __tls_get_addr. Will emit for fixups for tls_symbol in
// encodeInstruction.
#ifndef NDEBUG
// Verify that the callee is actually __tls_get_addr.
const SparcMCExpr *SExpr = dyn_cast<SparcMCExpr>(MO.getExpr());
assert(SExpr && SExpr->getSubExpr()->getKind() == MCExpr::SymbolRef &&
"Unexpected expression in TLS_CALL");
const MCSymbolRefExpr *SymExpr = cast<MCSymbolRefExpr>(SExpr->getSubExpr());
@ -169,15 +202,8 @@ getCallTargetOpValue(const MCInst &MI, unsigned OpNo,
return 0;
}
MCFixupKind fixupKind = (MCFixupKind)Sparc::fixup_sparc_call30;
if (const SparcMCExpr *SExpr = dyn_cast<SparcMCExpr>(MO.getExpr())) {
if (SExpr->getKind() == SparcMCExpr::VK_Sparc_WPLT30)
fixupKind = (MCFixupKind)Sparc::fixup_sparc_wplt30;
}
Fixups.push_back(MCFixup::create(0, MO.getExpr(), fixupKind));
MCFixupKind Kind = MCFixupKind(SExpr->getFixupKind());
Fixups.push_back(MCFixup::create(0, Expr, Kind));
return 0;
}

View File

@ -43,6 +43,8 @@ bool SparcMCExpr::printVariantKind(raw_ostream &OS, VariantKind Kind)
{
bool closeParen = true;
switch (Kind) {
default:
llvm_unreachable("Unhandled SparcMCExpr::VariantKind");
case VK_Sparc_None: closeParen = false; break;
case VK_Sparc_LO: OS << "%lo("; break;
case VK_Sparc_HI: OS << "%hi("; break;
@ -59,6 +61,7 @@ bool SparcMCExpr::printVariantKind(raw_ostream &OS, VariantKind Kind)
case VK_Sparc_GOT10: OS << "%lo("; break;
case VK_Sparc_GOT13: closeParen = false; break;
case VK_Sparc_13: closeParen = false; break;
case VK_Sparc_WDISP30: closeParen = false; break;
case VK_Sparc_WPLT30: closeParen = false; break;
case VK_Sparc_R_DISP32: OS << "%r_disp32("; break;
case VK_Sparc_TLS_GD_HI22: OS << "%tgd_hi22("; break;
@ -137,6 +140,7 @@ Sparc::Fixups SparcMCExpr::getFixupKind(SparcMCExpr::VariantKind Kind) {
case VK_Sparc_GOT13: return Sparc::fixup_sparc_got13;
case VK_Sparc_13: return Sparc::fixup_sparc_13;
case VK_Sparc_WPLT30: return Sparc::fixup_sparc_wplt30;
case VK_Sparc_WDISP30: return Sparc::fixup_sparc_call30;
case VK_Sparc_TLS_GD_HI22: return Sparc::fixup_sparc_tls_gd_hi22;
case VK_Sparc_TLS_GD_LO10: return Sparc::fixup_sparc_tls_gd_lo10;
case VK_Sparc_TLS_GD_ADD: return Sparc::fixup_sparc_tls_gd_add;

View File

@ -38,6 +38,7 @@ public:
VK_Sparc_GOT13,
VK_Sparc_13,
VK_Sparc_WPLT30,
VK_Sparc_WDISP30,
VK_Sparc_R_DISP32,
VK_Sparc_TLS_GD_HI22,
VK_Sparc_TLS_GD_LO10,

View File

@ -80,7 +80,7 @@ static MCOperand createSparcMCOperand(SparcMCExpr::VariantKind Kind,
}
static MCOperand createPCXCallOP(MCSymbol *Label,
MCContext &OutContext) {
return createSparcMCOperand(SparcMCExpr::VK_Sparc_None, Label, OutContext);
return createSparcMCOperand(SparcMCExpr::VK_Sparc_WDISP30, Label, OutContext);
}
static MCOperand createPCXRelExprOp(SparcMCExpr::VariantKind Kind,

View File

@ -939,7 +939,8 @@ SparcTargetLowering::LowerCall_32(TargetLowering::CallLoweringInfo &CLI,
// If the callee is a GlobalAddress node (quite common, every direct call is)
// turn it into a TargetGlobalAddress node so that legalize doesn't hack it.
// Likewise ExternalSymbol -> TargetExternalSymbol.
unsigned TF = isPositionIndependent() ? SparcMCExpr::VK_Sparc_WPLT30 : 0;
unsigned TF = isPositionIndependent() ? SparcMCExpr::VK_Sparc_WPLT30
: SparcMCExpr::VK_Sparc_WDISP30;
if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee))
Callee = DAG.getTargetGlobalAddress(G->getGlobal(), dl, MVT::i32, 0, TF);
else if (ExternalSymbolSDNode *E = dyn_cast<ExternalSymbolSDNode>(Callee))
@ -1242,7 +1243,8 @@ SparcTargetLowering::LowerCall_64(TargetLowering::CallLoweringInfo &CLI,
// Likewise ExternalSymbol -> TargetExternalSymbol.
SDValue Callee = CLI.Callee;
bool hasReturnsTwice = hasReturnsTwiceAttr(DAG, Callee, CLI.CB);
unsigned TF = isPositionIndependent() ? SparcMCExpr::VK_Sparc_WPLT30 : 0;
unsigned TF = isPositionIndependent() ? SparcMCExpr::VK_Sparc_WPLT30
: SparcMCExpr::VK_Sparc_WDISP30;
if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee))
Callee = DAG.getTargetGlobalAddress(G->getGlobal(), DL, PtrVT, 0, TF);
else if (ExternalSymbolSDNode *E = dyn_cast<ExternalSymbolSDNode>(Callee))

View File

@ -42,9 +42,9 @@ def : Pat<(i64 (sext i32:$val)), (SRAri $val, 0)>;
def : Pat<(i64 (and i64:$val, 0xffffffff)), (SRLri $val, 0)>;
def : Pat<(i64 (sext_inreg i64:$val, i32)), (SRAri $val, 0)>;
defm SLLX : F3_S<"sllx", 0b100101, 1, shl, i64, I64Regs>;
defm SRLX : F3_S<"srlx", 0b100110, 1, srl, i64, I64Regs>;
defm SRAX : F3_S<"srax", 0b100111, 1, sra, i64, I64Regs>;
defm SLLX : F3_S<"sllx", 0b100101, 1, shl, i64, shift_imm6, I64Regs>;
defm SRLX : F3_S<"srlx", 0b100110, 1, srl, i64, shift_imm6, I64Regs>;
defm SRAX : F3_S<"srax", 0b100111, 1, sra, i64, shift_imm6, I64Regs>;
} // Predicates = [Is64Bit]

View File

@ -224,13 +224,13 @@ class F3_Si<bits<2> opVal, bits<6> op3val, bit xVal, dag outs, dag ins,
// Define rr and ri shift instructions with patterns.
multiclass F3_S<string OpcStr, bits<6> Op3Val, bit XVal, SDNode OpNode,
ValueType VT, RegisterClass RC,
ValueType VT, ValueType SIT, RegisterClass RC,
InstrItinClass itin = IIC_iu_instr> {
def rr : F3_Sr<2, Op3Val, XVal, (outs RC:$rd), (ins RC:$rs1, IntRegs:$rs2),
!strconcat(OpcStr, " $rs1, $rs2, $rd"),
[(set VT:$rd, (OpNode VT:$rs1, i32:$rs2))],
itin>;
def ri : F3_Si<2, Op3Val, XVal, (outs RC:$rd), (ins RC:$rs1, i32imm:$shcnt),
def ri : F3_Si<2, Op3Val, XVal, (outs RC:$rd), (ins RC:$rs1, SIT:$shcnt),
!strconcat(OpcStr, " $rs1, $shcnt, $rd"),
[(set VT:$rd, (OpNode VT:$rs1, (i32 imm:$shcnt)))],
itin>;

View File

@ -113,6 +113,18 @@ def SETHIimm_not : PatLeaf<(i32 imm), [{
def ADDRrr : ComplexPattern<iPTR, 2, "SelectADDRrr", [], []>;
def ADDRri : ComplexPattern<iPTR, 2, "SelectADDRri", [frameindex], []>;
// Constrained operands for the shift operations.
class ShiftAmtImmAsmOperand<int Bits> : AsmOperandClass {
let Name = "ShiftAmtImm" # Bits;
let ParserMethod = "parseShiftAmtImm<" # Bits # ">";
}
def shift_imm5 : Operand<i32> {
let ParserMatchClass = ShiftAmtImmAsmOperand<5>;
}
def shift_imm6 : Operand<i32> {
let ParserMatchClass = ShiftAmtImmAsmOperand<6>;
}
// Address operands
def SparcMEMrrAsmOperand : AsmOperandClass {
let Name = "MEMrr";
@ -160,13 +172,20 @@ def bprtarget16 : Operand<OtherVT> {
let EncoderMethod = "getBranchOnRegTargetOpValue";
}
def SparcCallTargetAsmOperand : AsmOperandClass {
let Name = "CallTarget";
let ParserMethod = "parseCallTarget";
}
def calltarget : Operand<i32> {
let EncoderMethod = "getCallTargetOpValue";
let DecoderMethod = "DecodeCall";
let ParserMatchClass = SparcCallTargetAsmOperand;
}
def simm13Op : Operand<i32> {
let DecoderMethod = "DecodeSIMM13";
let EncoderMethod = "getSImm13OpValue";
}
// Operand for printing out a condition code.
@ -691,9 +710,9 @@ let Defs = [ICC] in {
}
// Section B.12 - Shift Instructions, p. 107
defm SLL : F3_12<"sll", 0b100101, shl, IntRegs, i32, simm13Op>;
defm SRL : F3_12<"srl", 0b100110, srl, IntRegs, i32, simm13Op>;
defm SRA : F3_12<"sra", 0b100111, sra, IntRegs, i32, simm13Op>;
defm SLL : F3_S<"sll", 0b100101, 0, shl, i32, shift_imm5, IntRegs>;
defm SRL : F3_S<"srl", 0b100110, 0, srl, i32, shift_imm5, IntRegs>;
defm SRA : F3_S<"sra", 0b100111, 0, sra, i32, shift_imm5, IntRegs>;
// Section B.13 - Add Instructions, p. 108
defm ADD : F3_12<"add", 0b000000, add, IntRegs, i32, simm13Op>;

View File

@ -14,3 +14,9 @@
! V8: instruction requires a CPU feature not currently enabled
! V9: invalid membar mask number
membar -127
! Test the boundary checks on the shift amount
! V8: immediate shift value out of range
sll %g1, 32, %g2
! V9: immediate shift value out of range
slx %g1, 64, %g2

View File

@ -18,6 +18,10 @@
! CHECK-NEXT: ! fixup A - offset: 0, value: %lo(sym), kind: fixup_sparc_lo10
call %g1+%lo(sym)
! CHECK-LABEL: .Ltmp0:
! CHECK: call .Ltmp0-4 ! encoding: [0b01AAAAAA,A,A,A]
call . - 4
! CHECK: jmp %g1+%i2 ! encoding: [0x81,0xc0,0x40,0x1a]
jmp %g1 + %i2
@ -31,6 +35,9 @@
! CHECK-NEXT: ! fixup A - offset: 0, value: %lo(sym), kind: fixup_sparc_lo10
jmp %g1+%lo(sym)
! CHECK: jmp sym ! encoding: [0x81,0xc0,0b001AAAAA,A]
jmp sym
! CHECK: jmpl %g1+%i2, %g2 ! encoding: [0x85,0xc0,0x40,0x1a]
jmpl %g1 + %i2, %g2

View File

@ -50,6 +50,10 @@
! CHECK-NEXT: ! fixup A - offset: 0, value: sym, kind: fixup_sparc_13
or %g1, sym, %g3
! CHECK: or %g1, sym+4, %g3 ! encoding: [0x86,0x10,0b011AAAAA,A]
! CHECK-NEXT: ! fixup A - offset: 0, value: sym+4, kind: fixup_sparc_13
or %g1, (sym+4), %g3
! This test needs to placed last in the file
! CHECK: .half a-.Ltmp0
.half a - .