Skip to content

Commit d97f17a

Browse files
authored
[MC][ARM] Fix crash when assembling Thumb 'movs r0,#foo'. (#115026)
If the assembler sees this instruction, understanding `foo` to be an external symbol, there's no relocation it can write that will put the whole value of `foo` into the 8-bit immediate field of the 16-bit Thumb add instruction. So it should report an error message pointing at the source line, and in LLVM 18, it did exactly that. But now the error is not reported, due to an indexing error in the operand list in `validateInstruction`, and instead the code continues to attempt assembly, ending up aborting at the `llvm_unreachable` at the end of `getHiLoImmOpValue`. In this commit I've fixed the index in the `ARM::tMOVi8` case of `validateInstruction`, and also the one for `tADDi8` which must cope with either the 2- or 3-operand form in the input assembly source. But also, while writing the test, I found that if you assemble for Armv7-M instead of Armv6-M, the instruction has opcode `t2ADDri` when it goes through `validateInstruction`, and only turns into `tMOVi8` later in `processInstruction`. Then it's too late for `validateInstruction` to report that error. So I've adjusted `processInstruction` to spot that case and inhibit the conversion.
1 parent 78f7ca0 commit d97f17a

File tree

3 files changed

+93
-10
lines changed

3 files changed

+93
-10
lines changed

llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8294,15 +8294,17 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst,
82948294
break;
82958295
}
82968296
case ARM::tADDi8: {
8297-
MCParsedAsmOperand &Op = *Operands[MnemonicOpsEndInd + 1];
8297+
int i = (Operands[MnemonicOpsEndInd + 1]->isImm()) ? MnemonicOpsEndInd + 1
8298+
: MnemonicOpsEndInd + 2;
8299+
MCParsedAsmOperand &Op = *Operands[i];
82988300
if (isARMMCExpr(Op) && !isThumbI8Relocation(Op))
82998301
return Error(Op.getStartLoc(),
83008302
"Immediate expression for Thumb adds requires :lower0_7:,"
83018303
" :lower8_15:, :upper0_7: or :upper8_15:");
83028304
break;
83038305
}
83048306
case ARM::tMOVi8: {
8305-
MCParsedAsmOperand &Op = *Operands[MnemonicOpsEndInd];
8307+
MCParsedAsmOperand &Op = *Operands[MnemonicOpsEndInd + 1];
83068308
if (isARMMCExpr(Op) && !isThumbI8Relocation(Op))
83078309
return Error(Op.getStartLoc(),
83088310
"Immediate expression for Thumb movs requires :lower0_7:,"
@@ -10709,14 +10711,26 @@ bool ARMAsmParser::processInstruction(MCInst &Inst,
1070910711
// the flags are compatible with the current IT status, use encoding T2
1071010712
// instead of T3. For compatibility with the system 'as'. Make sure the
1071110713
// wide encoding wasn't explicit.
10712-
if (Inst.getOperand(0).getReg() != Inst.getOperand(1).getReg() ||
10713-
!isARMLowRegister(Inst.getOperand(0).getReg()) ||
10714-
(Inst.getOperand(2).isImm() &&
10715-
(unsigned)Inst.getOperand(2).getImm() > 255) ||
10716-
Inst.getOperand(5).getReg() !=
10717-
(inITBlock() ? ARM::NoRegister : ARM::CPSR) ||
10718-
HasWideQualifier)
10719-
break;
10714+
if (HasWideQualifier)
10715+
break; // source code has asked for the 32-bit instruction
10716+
if (Inst.getOperand(0).getReg() != Inst.getOperand(1).getReg())
10717+
break; // tADDi8 can't take different input and output registers
10718+
if (!isARMLowRegister(Inst.getOperand(0).getReg()))
10719+
break; // high register that tADDi8 can't access
10720+
if (Inst.getOperand(5).getReg() !=
10721+
(inITBlock() ? ARM::NoRegister : ARM::CPSR))
10722+
break; // flag-modification would require overriding the IT state
10723+
if (Inst.getOperand(2).isImm()) {
10724+
if ((unsigned)Inst.getOperand(2).getImm() > 255)
10725+
break; // large immediate that tADDi8 can't contain
10726+
} else {
10727+
int i = (Operands[MnemonicOpsEndInd + 1]->isImm())
10728+
? MnemonicOpsEndInd + 1
10729+
: MnemonicOpsEndInd + 2;
10730+
MCParsedAsmOperand &Op = *Operands[i];
10731+
if (isARMMCExpr(Op) && !isThumbI8Relocation(Op))
10732+
break; // a type of non-immediate that tADDi8 can't represent
10733+
}
1072010734
MCInst TmpInst;
1072110735
TmpInst.setOpcode(Inst.getOpcode() == ARM::t2ADDri ?
1072210736
ARM::tADDi8 : ARM::tSUBi8);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: not llvm-mc --triple thumbv7m -filetype=obj -o /dev/null %s 2>&1 | FileCheck %s
2+
3+
// This test checks reporting of errors of the form "you should have
4+
// used :lower16: in this immediate field", when the errors are
5+
// discovered at the object-file output stage by checking the set of
6+
// available relocations.
7+
//
8+
// For errors that are reported earlier, when initially reading the
9+
// instructions, see lower-upper-errors.s.
10+
11+
// CHECK: [[@LINE+1]]:1: error: unsupported relocation
12+
adds r0, r0, #foo
13+
14+
// CHECK: [[@LINE+1]]:1: error: unsupported relocation
15+
add r9, r0, #foo
16+
17+
// CHECK: [[@LINE+1]]:1: error: expected relocatable expression
18+
movs r11, :upper8_15:#foo
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: not llvm-mc --triple thumbv6m %s 2>&1 | FileCheck %s --check-prefixes=CHECK,THUMB1
2+
// RUN: not llvm-mc --triple thumbv7m %s 2>&1 | FileCheck %s --check-prefixes=CHECK,THUMB2
3+
4+
// This test checks reporting of errors of the form "you should have
5+
// used :lower16: in this immediate field", during initial reading of
6+
// the source file.
7+
//
8+
// For errors that are reported at object-file output time, see
9+
// lower-upper-errors-2.s.
10+
11+
// CHECK: :[[@LINE+1]]:10: error: Immediate expression for Thumb movs requires :lower0_7:, :lower8_15:, :upper0_7: or :upper8_15:
12+
movs r0, #foo
13+
14+
// CHECK: :[[@LINE+1]]:10: error: Immediate expression for Thumb adds requires :lower0_7:, :lower8_15:, :upper0_7: or :upper8_15:
15+
adds r0, #foo
16+
17+
// THUMB2: :[[@LINE+1]]:10: error: immediate expression for mov requires :lower16: or :upper16
18+
movw r0, #foo
19+
20+
// THUMB2: :[[@LINE+1]]:10: error: immediate expression for mov requires :lower16: or :upper16
21+
movt r0, #foo
22+
23+
// With a Thumb2 wide add instruction available, this case isn't an error
24+
// while reading the source file. It only causes a problem when an object
25+
// file is output, and it turns out there's no suitable relocation to ask
26+
// for the value of an external symbol to be turned into a Thumb shifted
27+
// immediate field. And in this case the other errors in this source file
28+
// cause assembly to terminate before reaching the object-file output stage
29+
// (even if a test run had had -filetype=obj).
30+
31+
// THUMB1: :[[@LINE+2]]:14: error: Immediate expression for Thumb adds requires :lower0_7:, :lower8_15:, :upper0_7: or :upper8_15:
32+
// THUMB2-NOT: :[[@LINE+1]]:{{[0-9]+}}: error:
33+
adds r0, r0, #foo
34+
35+
// Similarly for this version, which _must_ be the wide encoding due
36+
// to the use of a high register and the lack of flag-setting.
37+
38+
// THUMB1: :[[@LINE+2]]:1: error: invalid instruction
39+
// THUMB2-NOT: :[[@LINE+1]]:{{[0-9]+}}: error:
40+
add r9, r0, #foo
41+
42+
// CHECK: :[[@LINE+1]]:10: error: Immediate expression for Thumb movs requires :lower0_7:, :lower8_15:, :upper0_7: or :upper8_15:
43+
movs r0, :lower16:#foo
44+
45+
// This is invalid in either architecture: in Thumb1 it can't use a
46+
// high register, and in Thumb2 it can't use :upper8_15:. But the
47+
// Thumb2 case won't cause an error until output.
48+
49+
// THUMB1: :[[@LINE+2]]:1: error: invalid instruction
50+
// THUMB2-NOT: :[[@LINE+1]]:{{[0-9]+}}: error:
51+
movs r11, :upper8_15:#foo

0 commit comments

Comments
 (0)