Commit 3f6b47d5 by John Porto

Subzero. ARM32. Removes memory legalization warts.

This CL removes two warts from the ARM32 backend: 1) during argument lowering, if a stack parameter is assigned a register, the backend creates a new Variable that references the stack location with the incoming argument, and _mov() it to the parameter. 2) During stack slot legalization, all _mov(Mem(), Reg) are converted to stores; and all _mov(Reg, Mem()) are converted to loads. BUG= https://code.google.com/p/nativeclient/issues/detail?id=4076 R=kschimpf@google.com Review URL: https://codereview.chromium.org/1457683004 .
parent 174531ec
......@@ -765,7 +765,7 @@ void InstARM32Mov::emitSingleDestMultiSource(const Cfg *Func) const {
namespace {
bool isVariableWithoutRegister(const Operand *Op) {
if (const auto *OpV = llvm::dyn_cast<const Variable>(Op)) {
if (const auto *OpV = llvm::dyn_cast<Variable>(Op)) {
return !OpV->hasReg();
}
return false;
......@@ -789,84 +789,57 @@ void InstARM32Mov::emitSingleDestSingleSource(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrEmit();
Variable *Dest = getDest();
if (Dest->hasReg()) {
Type Ty = Dest->getType();
Operand *Src0 = getSrc(0);
const bool IsVector = isVectorType(Ty);
const bool IsScalarFP = isScalarFloatingType(Ty);
const bool CoreVFPMove = isMoveBetweenCoreAndVFPRegisters(Dest, Src0);
const char *LoadOpcode = IsVector ? "vld1" : (IsScalarFP ? "vldr" : "ldr");
const bool IsVMove = (IsVector || IsScalarFP || CoreVFPMove);
const char *RegMovOpcode = IsVMove ? "vmov" : "mov";
const char *ActualOpcode = isMemoryAccess(Src0) ? LoadOpcode : RegMovOpcode;
// when vmov{c}'ing, we need to emit a width string. Otherwise, the
// assembler might be tempted to assume we want a vector vmov{c}, and that
// is disallowed because ARM.
const char *NoWidthString = "";
const char *WidthString =
isMemoryAccess(Src0)
? (IsVector ? ".64" : getWidthString(Ty))
: (!CoreVFPMove ? getVecWidthString(Ty) : NoWidthString);
Str << "\t" << ActualOpcode;
const bool IsVInst = IsVMove || IsVector || IsScalarFP;
if (IsVInst) {
Str << getPredicate() << WidthString;
} else {
Str << WidthString << getPredicate();
}
Str << "\t";
Dest->emit(Func);
Str << ", ";
Src0->emit(Func);
if (!Dest->hasReg()) {
llvm::report_fatal_error("mov can't store.");
}
Operand *Src0 = getSrc(0);
if (isMemoryAccess(Src0)) {
llvm::report_fatal_error("mov can't load.");
}
Type Ty = Dest->getType();
const bool IsVector = isVectorType(Ty);
const bool IsScalarFP = isScalarFloatingType(Ty);
const bool CoreVFPMove = isMoveBetweenCoreAndVFPRegisters(Dest, Src0);
const bool IsVMove = (IsVector || IsScalarFP || CoreVFPMove);
const char *Opcode = IsVMove ? "vmov" : "mov";
// when vmov{c}'ing, we need to emit a width string. Otherwise, the
// assembler might be tempted to assume we want a vector vmov{c}, and that
// is disallowed because ARM.
const char *WidthString = !CoreVFPMove ? getVecWidthString(Ty) : "";
Str << "\t" << Opcode;
if (IsVMove) {
Str << getPredicate() << WidthString;
} else {
Variable *Src0 = llvm::cast<Variable>(getSrc(0));
assert(Src0->hasReg());
Type Ty = Src0->getType();
const bool IsVector = isVectorType(Ty);
const bool IsScalarFP = isScalarFloatingType(Ty);
const char *ActualOpcode =
IsVector ? "vst1" : (IsScalarFP ? "vstr" : "str");
const char *WidthString = IsVector ? ".64" : getWidthString(Ty);
Str << "\t" << ActualOpcode;
const bool IsVInst = IsVector || IsScalarFP;
if (IsVInst) {
Str << getPredicate() << WidthString;
} else {
Str << WidthString << getPredicate();
}
Str << "\t";
Src0->emit(Func);
Str << ", ";
Dest->emit(Func);
Str << WidthString << getPredicate();
}
Str << "\t";
Dest->emit(Func);
Str << ", ";
Src0->emit(Func);
}
void InstARM32Mov::emitIASSingleDestSingleSource(const Cfg *Func) const {
auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>();
Variable *Dest = getDest();
Operand *Src0 = getSrc(0);
if (Dest->hasReg()) {
const Type DestTy = Dest->getType();
const bool DestIsVector = isVectorType(DestTy);
const bool DestIsScalarFP = isScalarFloatingType(DestTy);
const bool CoreVFPMove = isMoveBetweenCoreAndVFPRegisters(Dest, Src0);
if (DestIsVector || DestIsScalarFP || CoreVFPMove)
return Asm->setNeedsTextFixup();
if (isMemoryAccess(Src0)) {
// TODO(kschimpf) Figure out how to do ldr on CoreVPFMove? (see
// emitSingleDestSingleSource, local variable LoadOpcode).
return Asm->ldr(Dest, Src0, getPredicate(), Func->getTarget());
}
return Asm->mov(Dest, Src0, getPredicate());
} else {
const Type Src0Type = Src0->getType();
const bool Src0IsVector = isVectorType(Src0Type);
const bool Src0IsScalarFP = isScalarFloatingType(Src0Type);
const bool CoreVFPMove = isMoveBetweenCoreAndVFPRegisters(Dest, Src0);
if (Src0IsVector || Src0IsScalarFP || CoreVFPMove)
return Asm->setNeedsTextFixup();
return Asm->str(Src0, Dest, getPredicate(), Func->getTarget());
if (!Dest->hasReg()) {
llvm::report_fatal_error("mov can't store.");
}
if (isMemoryAccess(Src0)) {
llvm::report_fatal_error("mov can't load.");
}
const Type DestTy = Dest->getType();
const bool DestIsVector = isVectorType(DestTy);
const bool DestIsScalarFP = isScalarFloatingType(DestTy);
const bool CoreVFPMove = isMoveBetweenCoreAndVFPRegisters(Dest, Src0);
if (DestIsVector || DestIsScalarFP || CoreVFPMove)
return Asm->setNeedsTextFixup();
return Asm->mov(Dest, Src0, getPredicate());
}
void InstARM32Mov::emit(const Cfg *Func) const {
......
......@@ -475,9 +475,6 @@ void TargetARM32::emitVariable(const Variable *Var) const {
Offset += getStackAdjustment();
}
const Type VarTy = Var->getType();
if (!isLegalMemOffset(VarTy, Offset)) {
llvm::report_fatal_error("Illegal stack offset");
}
Str << "[" << getRegName(BaseRegNum, VarTy);
if (Offset != 0) {
Str << ", " << getConstantPrefix() << Offset;
......@@ -625,7 +622,7 @@ void TargetARM32::lowerArguments() {
// to copy Arg into its assigned register if applicable.
void TargetARM32::finishArgumentLowering(Variable *Arg, Variable *FramePtr,
size_t BasicFrameOffset,
size_t &InArgsSizeBytes) {
size_t *InArgsSizeBytes) {
if (auto *Arg64On32 = llvm::dyn_cast<Variable64On32>(Arg)) {
Variable *Lo = Arg64On32->getLo();
Variable *Hi = Arg64On32->getHi();
......@@ -634,26 +631,23 @@ void TargetARM32::finishArgumentLowering(Variable *Arg, Variable *FramePtr,
return;
}
Type Ty = Arg->getType();
InArgsSizeBytes = applyStackAlignmentTy(InArgsSizeBytes, Ty);
Arg->setStackOffset(BasicFrameOffset + InArgsSizeBytes);
InArgsSizeBytes += typeWidthInBytesOnStack(Ty);
// If the argument variable has been assigned a register, we need to load the
// value from the stack slot.
if (Arg->hasReg()) {
assert(Ty != IceType_i64);
// This should be simple, just load the parameter off the stack using a nice
// sp + imm addressing mode. Because ARM, we can't do that (e.g., VLDR, for
// fp types, cannot have an index register), so we legalize the memory
// operand instead.
auto *Mem = OperandARM32Mem::create(
Func, Ty, FramePtr, llvm::cast<ConstantInteger32>(
Ctx->getConstantInt32(Arg->getStackOffset())));
_mov(Arg, legalizeToReg(Mem, Arg->getRegNum()));
// This argument-copying instruction uses an explicit OperandARM32Mem
// operand instead of a Variable, so its fill-from-stack operation has to
// be tracked separately for statistics.
Ctx->statsUpdateFills();
assert(Ty != IceType_i64);
*InArgsSizeBytes = applyStackAlignmentTy(*InArgsSizeBytes, Ty);
const int32_t ArgStackOffset = BasicFrameOffset + *InArgsSizeBytes;
*InArgsSizeBytes += typeWidthInBytesOnStack(Ty);
if (!Arg->hasReg()) {
Arg->setStackOffset(ArgStackOffset);
return;
}
// If the argument variable has been assigned a register, we need to copy the
// value from the stack slot.
Variable *Parameter = Func->makeVariable(Ty);
Parameter->setMustNotHaveReg();
Parameter->setStackOffset(ArgStackOffset);
_mov(Arg, Parameter);
}
Type TargetARM32::stackSlotType() { return IceType_i32; }
......@@ -754,8 +748,6 @@ void TargetARM32::addProlog(CfgNode *Node) {
continue;
}
if (CalleeSaves[i] && RegsUsed[i]) {
// TODO(jvoung): do separate vpush for each floating point register
// segment and += 4, or 8 depending on type.
++NumCallee;
Variable *PhysicalRegister = getPhysicalRegister(i);
PreservedRegsSizeBytes +=
......@@ -837,7 +829,7 @@ void TargetARM32::addProlog(CfgNode *Node) {
InRegs = CC.I32InReg(&DummyReg);
}
if (!InRegs)
finishArgumentLowering(Arg, FramePtr, BasicFrameOffset, InArgsSizeBytes);
finishArgumentLowering(Arg, FramePtr, BasicFrameOffset, &InArgsSizeBytes);
}
// Fill in stack offsets for locals.
......@@ -981,11 +973,18 @@ Variable *TargetARM32::newBaseRegister(int32_t OriginalOffset,
return ScratchReg;
}
StackVariable *TargetARM32::legalizeStackSlot(Type Ty, int32_t Offset,
int32_t StackAdjust,
Variable *OrigBaseReg,
Variable **NewBaseReg,
int32_t *NewBaseOffset) {
OperandARM32Mem *TargetARM32::createMemOperand(Type Ty, int32_t Offset,
int32_t StackAdjust,
Variable *OrigBaseReg,
Variable **NewBaseReg,
int32_t *NewBaseOffset) {
if (isLegalMemOffset(Ty, Offset + StackAdjust)) {
return OperandARM32Mem::create(
Func, Ty, OrigBaseReg, llvm::cast<ConstantInteger32>(
Ctx->getConstantInt32(Offset + StackAdjust)),
OperandARM32Mem::Offset);
}
if (*NewBaseReg == nullptr) {
*NewBaseReg = newBaseRegister(Offset, StackAdjust, OrigBaseReg);
*NewBaseOffset = Offset + StackAdjust;
......@@ -998,18 +997,15 @@ StackVariable *TargetARM32::legalizeStackSlot(Type Ty, int32_t Offset,
OffsetDiff = 0;
}
StackVariable *NewDest = Func->makeVariable<StackVariable>(Ty);
NewDest->setMustNotHaveReg();
NewDest->setBaseRegNum((*NewBaseReg)->getRegNum());
NewDest->setStackOffset(OffsetDiff);
return NewDest;
return OperandARM32Mem::create(
Func, Ty, *NewBaseReg,
llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(OffsetDiff)),
OperandARM32Mem::Offset);
}
void TargetARM32::legalizeMovStackAddrImm(InstARM32Mov *MovInstr,
int32_t StackAdjust,
Variable *OrigBaseReg,
Variable **NewBaseReg,
int32_t *NewBaseOffset) {
void TargetARM32::legalizeMov(InstARM32Mov *MovInstr, int32_t StackAdjust,
Variable *OrigBaseReg, Variable **NewBaseReg,
int32_t *NewBaseOffset) {
Variable *Dest = MovInstr->getDest();
assert(Dest != nullptr);
Type DestTy = Dest->getType();
......@@ -1017,6 +1013,7 @@ void TargetARM32::legalizeMovStackAddrImm(InstARM32Mov *MovInstr,
Operand *Src = MovInstr->getSrc(0);
Type SrcTy = Src->getType();
(void)SrcTy;
assert(SrcTy != IceType_i64);
if (MovInstr->isMultiDest() || MovInstr->isMultiSource())
......@@ -1024,40 +1021,29 @@ void TargetARM32::legalizeMovStackAddrImm(InstARM32Mov *MovInstr,
bool Legalized = false;
if (!Dest->hasReg()) {
assert(llvm::cast<Variable>(Src)->hasReg());
auto *const SrcR = llvm::cast<Variable>(Src);
assert(SrcR->hasReg());
const int32_t Offset = Dest->getStackOffset();
if (!isLegalMemOffset(DestTy, Offset + StackAdjust)) {
Legalized = true;
Dest = legalizeStackSlot(DestTy, Offset, StackAdjust, OrigBaseReg,
NewBaseReg, NewBaseOffset);
}
// This is a _mov(Mem(), Variable), i.e., a store.
_str(SrcR, createMemOperand(DestTy, Offset, StackAdjust, OrigBaseReg,
NewBaseReg, NewBaseOffset),
MovInstr->getPredicate());
// _str() does not have a Dest, so we add a fake-def(Dest).
Context.insert(InstFakeDef::create(Func, Dest));
Legalized = true;
} else if (auto *Var = llvm::dyn_cast<Variable>(Src)) {
if (!Var->hasReg()) {
const int32_t Offset = Var->getStackOffset();
if (!isLegalMemOffset(SrcTy, Offset + StackAdjust)) {
Legalized = true;
Src = legalizeStackSlot(SrcTy, Offset, StackAdjust, OrigBaseReg,
NewBaseReg, NewBaseOffset);
}
}
} else if (auto *Mem = llvm::dyn_cast<OperandARM32Mem>(Src)) {
if (ConstantInteger32 *OffsetOp = Mem->getOffset()) {
const int32_t Offset = OffsetOp->getValue();
if (!isLegalMemOffset(SrcTy, Offset + StackAdjust)) {
assert(Mem->getBase()->hasReg());
assert(Mem->getBase()->getRegNum() == (int32_t)getFrameOrStackReg());
Legalized = true;
Src = legalizeStackSlot(SrcTy, Offset, StackAdjust, OrigBaseReg,
NewBaseReg, NewBaseOffset);
}
_ldr(Dest, createMemOperand(DestTy, Offset, StackAdjust, OrigBaseReg,
NewBaseReg, NewBaseOffset),
MovInstr->getPredicate());
Legalized = true;
}
}
if (Legalized) {
if (MovInstr->isDestRedefined()) {
_mov_redefined(Dest, Src, MovInstr->getPredicate());
} else {
_mov(Dest, Src, MovInstr->getPredicate());
_set_dest_redefined();
}
MovInstr->setDeleted();
}
......@@ -1075,11 +1061,6 @@ void TargetARM32::legalizeStackSlots() {
// clobber the flags register.
Func->dump("Before legalizeStackSlots");
assert(hasComputedFrame());
// Early exit, if SpillAreaSizeBytes is really small.
// TODO(jpp): this is not safe -- loads and stores of q registers can't have
// offsets.
if (isLegalMemOffset(IceType_v4i32, SpillAreaSizeBytes))
return;
Variable *OrigBaseReg = getPhysicalRegister(getFrameOrStackReg());
int32_t StackAdjust = 0;
// Do a fairly naive greedy clustering for now. Pick the first stack slot
......@@ -1122,11 +1103,9 @@ void TargetARM32::legalizeStackSlots() {
}
}
// The Lowering ensures that ldr and str always have legal Mem operands.
// The only other instruction that may access memory is mov.
if (auto *MovInstr = llvm::dyn_cast<InstARM32Mov>(CurInstr)) {
legalizeMovStackAddrImm(MovInstr, StackAdjust, OrigBaseReg, &NewBaseReg,
&NewBaseOffset);
legalizeMov(MovInstr, StackAdjust, OrigBaseReg, &NewBaseReg,
&NewBaseOffset);
}
}
}
......@@ -4600,7 +4579,11 @@ Variable *TargetARM32::makeVectorOfZeros(Type Ty, int32_t RegNum) {
Variable *TargetARM32::copyToReg(Operand *Src, int32_t RegNum) {
Type Ty = Src->getType();
Variable *Reg = makeReg(Ty, RegNum);
_mov(Reg, Src);
if (auto *Mem = llvm::dyn_cast<OperandARM32Mem>(Src)) {
_ldr(Reg, Mem);
} else {
_mov(Reg, Src);
}
return Reg;
}
......@@ -4641,7 +4624,6 @@ Operand *TargetARM32::legalize(Operand *From, LegalMask Allowed,
Variable *RegIndex = nullptr;
assert(Base);
RegBase = legalizeToReg(Base);
bool InvalidImm = false;
assert(Ty < MemTraitsSize);
if (Index) {
assert(Offset == nullptr);
......@@ -4653,12 +4635,7 @@ Operand *TargetARM32::legalize(Operand *From, LegalMask Allowed,
static constexpr bool ZeroExt = false;
assert(MemTraits[Ty].CanHaveImm);
if (!OperandARM32Mem::canHoldOffset(Ty, ZeroExt, Offset->getValue())) {
assert(RegBase->hasReg());
assert(RegBase->getRegNum() == (int32_t)getFrameOrStackReg());
// We are a bit more lenient with invalid immediate when accessing the
// stack here, and then rely on legalizeStackSlots() to fix things as
// appropriate.
InvalidImm = true;
llvm::report_fatal_error("Invalid memory offset.");
}
}
......@@ -4679,15 +4656,7 @@ Operand *TargetARM32::legalize(Operand *From, LegalMask Allowed,
From = Mem;
} else {
Variable *Reg = makeReg(Ty, RegNum);
if (InvalidImm) {
// If Mem has an invalid immediate, we legalize it to a Reg using mov
// instead of ldr because legalizeStackSlots() will later kick in and
// fix the immediate for us.
_mov(Reg, Mem);
} else {
_ldr(Reg, Mem);
}
_ldr(Reg, Mem);
From = Reg;
}
return From;
......
......@@ -135,7 +135,7 @@ public:
Operand *loOperand(Operand *Operand);
Operand *hiOperand(Operand *Operand);
void finishArgumentLowering(Variable *Arg, Variable *FramePtr,
size_t BasicFrameOffset, size_t &InArgsSizeBytes);
size_t BasicFrameOffset, size_t *InArgsSizeBytes);
bool hasCPUFeature(TargetARM32Features::ARM32InstructionSet I) const {
return CPUFeatures.hasFeature(I);
......@@ -384,6 +384,7 @@ protected:
// an assert around just in case there is some untested code path where Dest
// is nullptr.
assert(Dest != nullptr);
assert(!llvm::isa<OperandARM32Mem>(Src0));
auto *Instr = InstARM32Mov::create(Func, Dest, Src0, Pred);
Context.insert(Instr);
......@@ -794,15 +795,15 @@ protected:
CondARM32::Cond Pred = CondARM32::AL) {
Context.insert(InstARM32Vcmp::create(Func, Src0, FpZero, Pred));
}
void _veor(Variable *Dest, Variable *Src0, Variable *Src1) {
Context.insert(InstARM32Veor::create(Func, Dest, Src0, Src1));
}
void _vmrs(CondARM32::Cond Pred = CondARM32::AL) {
Context.insert(InstARM32Vmrs::create(Func, Pred));
}
void _vmul(Variable *Dest, Variable *Src0, Variable *Src1) {
Context.insert(InstARM32Vmul::create(Func, Dest, Src0, Src1));
}
void _veor(Variable *Dest, Variable *Src0, Variable *Src1) {
Context.insert(InstARM32Veor::create(Func, Dest, Src0, Src1));
}
void _vsqrt(Variable *Dest, Variable *Src,
CondARM32::Cond Pred = CondARM32::AL) {
Context.insert(InstARM32Vsqrt::create(Func, Dest, Src, Pred));
......@@ -821,17 +822,27 @@ protected:
// [OrigBaseReg, +/- Offset+StackAdjust].
Variable *newBaseRegister(int32_t Offset, int32_t StackAdjust,
Variable *OrigBaseReg);
/// Creates a new, legal StackVariable w.r.t. ARM's Immediate requirements.
/// This method is not very smart: it will always create and return a new
/// StackVariable, even if Offset + StackAdjust is encodable.
StackVariable *legalizeStackSlot(Type Ty, int32_t Offset, int32_t StackAdjust,
Variable *OrigBaseReg, Variable **NewBaseReg,
int32_t *NewBaseOffset);
/// Legalizes Mov if its Source (or Destination) contains an invalid
/// immediate.
void legalizeMovStackAddrImm(InstARM32Mov *Mov, int32_t StackAdjust,
Variable *OrigBaseReg, Variable **NewBaseReg,
int32_t *NewBaseOffset);
/// Creates a new, legal OperandARM32Mem for accessing OrigBase + Offset +
/// StackAdjust. The returned mem operand is a legal operand for accessing
/// memory that is of type Ty.
///
/// If [OrigBaseReg, #Offset+StackAdjust] is encodable, then the method
/// returns a Mem operand expressing it. Otherwise,
///
/// if [*NewBaseReg, #Offset+StackAdjust-*NewBaseOffset] is encodable, the
/// method will return that. Otherwise,
///
/// a new base register ip=OrigBaseReg+Offset+StackAdjust is created, and the
/// method returns [ip, #0].
OperandARM32Mem *createMemOperand(Type Ty, int32_t Offset,
int32_t StackAdjust, Variable *OrigBaseReg,
Variable **NewBaseReg,
int32_t *NewBaseOffset);
/// Legalizes Mov if its Source (or Destination) is a spilled Variable. Moves
/// to memory become store instructions, and moves from memory, loads.
void legalizeMov(InstARM32Mov *Mov, int32_t StackAdjust,
Variable *OrigBaseReg, Variable **NewBaseReg,
int32_t *NewBaseOffset);
TargetARM32Features CPUFeatures;
bool UsesFramePointer = false;
......
......@@ -24,7 +24,9 @@ define internal void @mult_fwd_branches(i32 %a, i32 %b) {
; ASM-NEXT: sub sp, sp, #12
; ASM-NEXT: str r0, [sp, #8]
; ASM-NEXT: # [sp, #8] = def.pseudo
; ASM-NEXT: str r1, [sp, #4]
; ASM-NEXT: # [sp, #4] = def.pseudo
; DIS-LABEL:00000000 <mult_fwd_branches>:
......@@ -58,6 +60,7 @@ define internal void @mult_fwd_branches(i32 %a, i32 %b) {
; ASM-NEXT: cmp r1, r2
; ASM-NEXT: movlt r0, #1
; ASM-NEXT: strb r0, [sp]
; ASM-NEXT: # [sp] = def.pseudo
; DIS-NEXT: c: e3a00000
; DIS-NEXT: 10: e59d1008
......@@ -172,5 +175,4 @@ end:
; IASM-NEXT: .byte 0xff
; IASM-NEXT: .byte 0x2f
; IASM-NEXT: .byte 0xe1
}
......@@ -35,6 +35,7 @@ define internal i32 @add1ToR0(i32 %p) {
; IASM-NEXT: .byte 0xe2
; ASM-NEXT: str r0, [sp, #4]
; ASM-NEXT: # [sp, #4] = def.pseudo
; DIS-NEXT: 4: e58d0004
; IASM-NEXT: .byte 0x4
; IASM-NEXT: .byte 0x0
......@@ -56,6 +57,7 @@ define internal i32 @add1ToR0(i32 %p) {
; IASM-NEXT: .byte 0xe2
; ASM-NEXT: str r0, [sp]
; ASM-NEXT: # [sp] = def.pseudo
; DIS-NEXT: 10: e58d0000
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
......
......@@ -29,7 +29,7 @@ entry:
; ASM-NEXT: movw ip, #4092
; ASM-NEXT: sub sp, sp, ip
; ASM-NEXT: str r0, [sp, #4088]
; ASM-NEXT: # [sp, #4088] = def.pseudo
; DIS-LABEL: 00000000 <foo>:
; DIS-NEXT: 0: e300cffc
; DIS-NEXT: 4: e04dd00c
......@@ -59,6 +59,7 @@ entry:
; ASM-NEXT: ldr r1, [sp, #4088]
; ASM-NEXT: mul r0, r0, r1
; ASM-NEXT: str r0, [sp, #4084]
; ASM-NEXT: # [sp, #4084] = def.pseudo
; DIS-NEXT: c: e59d0ff8
; DIS-NEXT: 10: e59d1ff8
......
......@@ -57,7 +57,7 @@ entry:
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0xb
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: # [fp, #-4] = def.pseudo
br label %next
; ASM-NEXT: b .Ltest_vla_in_loop$next
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment