Skip to content

Commit e015626

Browse files
committed
MC: Allow .set to reassign non-MCConstantExpr expressions
GNU Assembler supports symbol reassignment via .set, .equ, or =. However, LLVM's integrated assembler only allows reassignment for MCConstantExpr cases, as it struggles with scenarios like: ``` .data .set x, 0 .long x // reference the first instance x = .-.data .long x // reference the second instance .set x,.-.data .long x // reference the third instance ``` Between two assignments binds, we cannot ensure that a reference binds to the earlier assignment. We use MCSymbol::IsUsed and other conditions to reject potentially unsafe reassignments, but certain MCConstantExpr uses could be unsafe as well. This patch enables reassignment by cloning the symbol upon reassignment and updating the symbol table. Existing references to the original symbol remain unchanged, and the original symbol is excluded from the emitted symbol table.
1 parent de93f7e commit e015626

File tree

10 files changed

+92
-68
lines changed

10 files changed

+92
-68
lines changed

llvm/include/llvm/MC/MCContext.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,11 @@ class MCContext {
492492
/// Get the symbol for \p Name, or null.
493493
MCSymbol *lookupSymbol(const Twine &Name) const;
494494

495+
/// Clone a symbol for the .set directive, replacing it in the symbol table.
496+
/// Existing references to the original symbol remain unchanged, and the
497+
/// original symbol is not emitted to the symbol table.
498+
MCSymbol *cloneSymbol(MCSymbol &Sym);
499+
495500
/// Set value for a symbol.
496501
void setSymbolValue(MCStreamer &Streamer, const Twine &Sym, uint64_t Val);
497502

llvm/include/llvm/MC/MCSymbol.h

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,6 @@ class MCSymbol {
9090
/// True if this symbol can be redefined.
9191
unsigned IsRedefinable : 1;
9292

93-
/// IsUsed - True if this symbol has been used.
94-
mutable unsigned IsUsed : 1;
95-
9693
mutable unsigned IsRegistered : 1;
9794

9895
/// True if this symbol is visible outside this translation unit. Note: ELF
@@ -165,16 +162,19 @@ class MCSymbol {
165162
};
166163

167164
MCSymbol(SymbolKind Kind, const MCSymbolTableEntry *Name, bool isTemporary)
168-
: IsTemporary(isTemporary), IsRedefinable(false), IsUsed(false),
169-
IsRegistered(false), IsExternal(false), IsPrivateExtern(false),
170-
IsWeakExternal(false), Kind(Kind), IsUsedInReloc(false), IsResolving(0),
165+
: IsTemporary(isTemporary), IsRedefinable(false), IsRegistered(false),
166+
IsExternal(false), IsPrivateExtern(false), IsWeakExternal(false),
167+
Kind(Kind), IsUsedInReloc(false), IsResolving(0),
171168
SymbolContents(SymContentsUnset), CommonAlignLog2(0), Flags(0) {
172169
Offset = 0;
173170
HasName = !!Name;
174171
if (Name)
175172
getNameEntryPtr() = Name;
176173
}
177174

175+
MCSymbol(const MCSymbol &) = default;
176+
MCSymbol &operator=(const MCSymbol &) = delete;
177+
178178
// Provide custom new/delete as we will only allocate space for a name
179179
// if we need one.
180180
void *operator new(size_t s, const MCSymbolTableEntry *Name, MCContext &Ctx);
@@ -201,9 +201,6 @@ class MCSymbol {
201201
}
202202

203203
public:
204-
MCSymbol(const MCSymbol &) = delete;
205-
MCSymbol &operator=(const MCSymbol &) = delete;
206-
207204
/// getName - Get the symbol name.
208205
StringRef getName() const {
209206
if (!HasName)
@@ -224,9 +221,6 @@ class MCSymbol {
224221
/// isTemporary - Check if this is an assembler temporary symbol.
225222
bool isTemporary() const { return IsTemporary; }
226223

227-
/// isUsed - Check if this is used.
228-
bool isUsed() const { return IsUsed; }
229-
230224
/// Check if this symbol is redefinable.
231225
bool isRedefinable() const { return IsRedefinable; }
232226
/// Mark this symbol as redefinable.
@@ -306,10 +300,9 @@ class MCSymbol {
306300
return SymbolContents == SymContentsVariable;
307301
}
308302

309-
/// getVariableValue - Get the value for variable symbols.
303+
/// Get the expression of the variable symbol.
310304
const MCExpr *getVariableValue(bool SetUsed = true) const {
311305
assert(isVariable() && "Invalid accessor!");
312-
IsUsed |= SetUsed;
313306
return Value;
314307
}
315308

@@ -404,7 +397,7 @@ class MCSymbol {
404397
return Fragment;
405398
// If the symbol is a non-weak alias, get information about
406399
// the aliasee. (Don't try to resolve weak aliases.)
407-
Fragment = getVariableValue(false)->findAssociatedFragment();
400+
Fragment = getVariableValue()->findAssociatedFragment();
408401
return Fragment;
409402
}
410403

llvm/lib/MC/ELFObjectWriter.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,8 +446,7 @@ void ELFWriter::writeSymbol(SymbolTableWriter &Writer, uint32_t StringIndex,
446446
// needs. MCBinaryExpr is not handled.
447447
const MCSymbolELF *Sym = &Symbol;
448448
while (Sym->isVariable()) {
449-
if (auto *Expr =
450-
dyn_cast<MCSymbolRefExpr>(Sym->getVariableValue(false))) {
449+
if (auto *Expr = dyn_cast<MCSymbolRefExpr>(Sym->getVariableValue())) {
451450
Sym = cast<MCSymbolELF>(&Expr->getSymbol());
452451
if (!Sym->getSize())
453452
continue;

llvm/lib/MC/MCContext.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,35 @@ MCSymbol *MCContext::createSymbolImpl(const MCSymbolTableEntry *Name,
309309
MCSymbol(MCSymbol::SymbolKindUnset, Name, IsTemporary);
310310
}
311311

312+
MCSymbol *MCContext::cloneSymbol(MCSymbol &Sym) {
313+
MCSymbol *NewSym = nullptr;
314+
auto Name = Sym.getNameEntryPtr();
315+
switch (getObjectFileType()) {
316+
case MCContext::IsCOFF:
317+
NewSym = new (Name, *this) MCSymbolCOFF(cast<MCSymbolCOFF>(Sym));
318+
break;
319+
case MCContext::IsELF:
320+
NewSym = new (Name, *this) MCSymbolELF(cast<MCSymbolELF>(Sym));
321+
break;
322+
case MCContext::IsMachO:
323+
NewSym = new (Name, *this) MCSymbolMachO(cast<MCSymbolMachO>(Sym));
324+
break;
325+
default:
326+
reportFatalUsageError(".set redefinition is not supported");
327+
break;
328+
}
329+
// Set the name and redirect the `Symbols` entry to `NewSym`.
330+
NewSym->getNameEntryPtr() = Name;
331+
const_cast<MCSymbolTableEntry *>(Name)->second.Symbol = NewSym;
332+
// Ensure the next `registerSymbol` call will add the new symbol to `Symbols`.
333+
NewSym->setIsRegistered(false);
334+
335+
// Ensure the original symbol is not emitted to the symbol table.
336+
Sym.IsTemporary = true;
337+
Sym.setExternal(false);
338+
return NewSym;
339+
}
340+
312341
MCSymbol *MCContext::createRenamableSymbol(const Twine &Name,
313342
bool AlwaysAddSuffix,
314343
bool IsTemporary) {

llvm/lib/MC/MCExpr.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -479,8 +479,6 @@ static bool canExpand(const MCSymbol &Sym, bool InSet) {
479479
if (Sym.isWeakExternal())
480480
return false;
481481

482-
Sym.getVariableValue(true);
483-
484482
if (InSet)
485483
return true;
486484
return !Sym.isInSection();
@@ -508,7 +506,6 @@ bool MCExpr::evaluateAsRelocatableImpl(MCValue &Res, const MCAssembler *Asm,
508506
Asm->getContext().reportError(
509507
Sym.getVariableValue()->getLoc(),
510508
"cyclic dependency detected for symbol '" + Sym.getName() + "'");
511-
Sym.IsUsed = false;
512509
Sym.setVariableValue(MCConstantExpr::create(0, Asm->getContext()));
513510
}
514511
return false;

llvm/lib/MC/MCParser/AsmParser.cpp

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,14 +1231,14 @@ bool AsmParser::parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc,
12311231
// If this is an absolute variable reference, substitute it now to preserve
12321232
// semantics in the face of reassignment.
12331233
if (Sym->isVariable()) {
1234-
auto V = Sym->getVariableValue(/*SetUsed*/ false);
1234+
auto V = Sym->getVariableValue();
12351235
bool DoInline = isa<MCConstantExpr>(V) && !Variant;
12361236
if (auto TV = dyn_cast<MCTargetExpr>(V))
12371237
DoInline = TV->inlineAssignedExpr();
12381238
if (DoInline) {
12391239
if (Variant)
12401240
return Error(EndLoc, "unexpected modifier on variable reference");
1241-
Res = Sym->getVariableValue(/*SetUsed*/ false);
1241+
Res = Sym->getVariableValue();
12421242
return false;
12431243
}
12441244
}
@@ -6332,34 +6332,20 @@ bool parseAssignmentExpression(StringRef Name, bool allow_redef,
63326332
SMLoc EqualLoc = Parser.getTok().getLoc();
63336333
if (Parser.parseExpression(Value))
63346334
return Parser.TokError("missing expression");
6335-
6336-
// Note: we don't count b as used in "a = b". This is to allow
6337-
// a = b
6338-
// b = c
6339-
63406335
if (Parser.parseEOL())
63416336
return true;
63426337

63436338
// Validate that the LHS is allowed to be a variable (either it has not been
63446339
// used as a symbol, or it is an absolute symbol).
63456340
Sym = Parser.getContext().lookupSymbol(Name);
63466341
if (Sym) {
6347-
// Diagnose assignment to a label.
6348-
//
6349-
// FIXME: Diagnostics. Note the location of the definition as a label.
6350-
// FIXME: Diagnose assignment to protected identifier (e.g., register name).
63516342
if (!Sym->isUnset() && (!allow_redef || !Sym->isRedefinable()))
63526343
return Parser.Error(EqualLoc, "redefinition of '" + Name + "'");
6353-
else if (Sym->isUndefined() && !Sym->isUsed() && !Sym->isVariable())
6354-
; // Allow redefinitions of undefined symbols only used in directives.
6355-
else if (Sym->isVariable() && !Sym->isUsed() && allow_redef)
6356-
; // Allow redefinitions of variables that haven't yet been used.
6357-
else if (!Sym->isVariable())
6358-
return Parser.Error(EqualLoc, "invalid assignment to '" + Name + "'");
6359-
else if (!isa<MCConstantExpr>(Sym->getVariableValue()))
6360-
return Parser.Error(EqualLoc,
6361-
"invalid reassignment of non-absolute variable '" +
6362-
Name + "'");
6344+
// If the symbol is redefinable, clone it and update the symbol table
6345+
// to the new symbol. Existing references to the original symbol remain
6346+
// unchanged.
6347+
if (Sym->isRedefinable())
6348+
Sym = Parser.getContext().cloneSymbol(*Sym);
63636349
} else if (Name == ".") {
63646350
Parser.getStreamer().emitValueToOffset(Value, 0, EqualLoc);
63656351
return false;

llvm/lib/MC/MCParser/MasmParser.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,12 +1486,12 @@ bool MasmParser::parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc,
14861486
// If this is an absolute variable reference, substitute it now to preserve
14871487
// semantics in the face of reassignment.
14881488
if (Sym->isVariable()) {
1489-
auto V = Sym->getVariableValue(/*SetUsed=*/false);
1489+
auto V = Sym->getVariableValue();
14901490
bool DoInline = isa<MCConstantExpr>(V);
14911491
if (auto TV = dyn_cast<MCTargetExpr>(V))
14921492
DoInline = TV->inlineAssignedExpr();
14931493
if (DoInline) {
1494-
Res = Sym->getVariableValue(/*SetUsed=*/false);
1494+
Res = Sym->getVariableValue();
14951495
return false;
14961496
}
14971497
}
@@ -3012,9 +3012,9 @@ bool MasmParser::parseDirectiveEquate(StringRef IDVal, StringRef Name,
30123012
MCSymbol *Sym = getContext().getOrCreateSymbol(Var.Name);
30133013

30143014
const MCConstantExpr *PrevValue =
3015-
Sym->isVariable() ? dyn_cast_or_null<MCConstantExpr>(
3016-
Sym->getVariableValue(/*SetUsed=*/false))
3017-
: nullptr;
3015+
Sym->isVariable()
3016+
? dyn_cast_or_null<MCConstantExpr>(Sym->getVariableValue())
3017+
: nullptr;
30183018
if (Var.IsText || !PrevValue || PrevValue->getValue() != Value) {
30193019
switch (Var.Redefinable) {
30203020
case Variable::NOT_REDEFINABLE:

llvm/lib/MC/MCSymbol.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ void *MCSymbol::operator new(size_t s, const MCSymbolTableEntry *Name,
4545
}
4646

4747
void MCSymbol::setVariableValue(const MCExpr *Value) {
48-
assert(!IsUsed && "Cannot set a variable that has already been used.");
4948
assert(Value && "Invalid variable value!");
5049
assert((SymbolContents == SymContentsUnset ||
5150
SymbolContents == SymContentsVariable) &&

llvm/test/MC/ARM/thumb_set-diagnostics.s

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
@ RUN: not llvm-mc -triple armv7-eabi -o /dev/null %s 2>&1 | FileCheck %s
2-
@ RUN: not llvm-mc -filetype=obj -triple=armv7-eabi -o /dev/null %s --defsym LOOP=1 2>&1 | FileCheck %s --check-prefix=ERR2 --implicit-check-not=error:
1+
@ RUN: rm -rf %t && split-file %s %t --leading-lines && cd %t
2+
@ RUN: not llvm-mc -triple armv7-eabi -o /dev/null a.s 2>&1 | FileCheck %s
3+
@ RUN: not llvm-mc -filetype=obj -triple=armv7-eabi -o /dev/null redef.s 2>&1 | FileCheck %s --check-prefix=ERR
4+
@ RUN: not llvm-mc -filetype=obj -triple=armv7-eabi -o /dev/null cycle.s 2>&1 | FileCheck %s --check-prefix=ERR2 --implicit-check-not=error:
35

6+
//--- a.s
47
.syntax unified
58

69
.thumb
7-
8-
.ifndef LOOP
910
.thumb_set
1011

1112
@ CHECK: error: expected identifier after '.thumb_set'
@@ -42,29 +43,20 @@
4243
@ CHECK: .thumb_set trailer_trash, 0x11fe1e55,
4344
@ CHECK: ^
4445

46+
//--- redef.s
4547
.type alpha,%function
4648
alpha:
4749
nop
48-
4950
.type beta,%function
5051
beta:
51-
bkpt
52-
53-
.thumb_set beta, alpha
54-
55-
@ CHECK: error: redefinition of 'beta'
56-
@ CHECK: .thumb_set beta, alpha
57-
@ CHECK: ^
52+
.thumb_set beta, alpha
53+
@ ERR: [[#@LINE-1]]:18: error: redefinition of 'beta'
5854

5955
variable_result = alpha + 1
6056
.long variable_result
6157
.thumb_set variable_result, 1
6258

63-
@ CHECK: error: invalid reassignment of non-absolute variable 'variable_result'
64-
@ CHECK: .thumb_set variable_result, 1
65-
@ CHECK: ^
66-
67-
.else
59+
//--- cycle.s
6860
.type recursive_use,%function
6961
.thumb_set recursive_use, recursive_use + 1
7062
@ ERR2: [[#@LINE-1]]:41: error: cyclic dependency detected for symbol 'recursive_use'
@@ -74,4 +66,3 @@ beta:
7466
.thumb_set recursive_use2, recursive_use2 + 1
7567
@ ERR2: [[#@LINE-1]]:43: error: cyclic dependency detected for symbol 'recursive_use2'
7668
@ ERR2: [[#@LINE-2]]:43: error: expression could not be evaluated
77-
.endif

llvm/test/MC/AsmParser/redef.s

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
1-
# RUN: not llvm-mc -filetype=obj -triple=x86_64 %s -o /dev/null 2>&1 | FileCheck %s --implicit-check-not=error:
1+
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t
2+
# RUN: llvm-objdump -ts %t | FileCheck %s
3+
4+
# CHECK: SYMBOL TABLE:
5+
# CHECK-NEXT: 0000000000000000 l .text 0000000000000000 l
6+
# CHECK-NEXT: 0000000000000008 l *ABS* 0000000000000000 x
7+
# CHECK-NEXT: 0000000000000002 l *ABS* 0000000000000000 b
8+
# CHECK-NEXT: 0000000000000003 l *ABS* 0000000000000000 c
9+
# CHECK-NEXT: ffffffffffffffff l *ABS* 0000000000000000 a
10+
# CHECK-NEXT: 0000000000000000 g .text 0000000000000000 l_v
11+
# CHECK: Contents of section .data:
12+
# CHECK-NEXT: 0000 00000000 04000000 08000000 .
13+
# CHECK-NEXT: Contents of section .data1:
14+
# CHECK-NEXT: 0000 010203ff 0203 ......
215

316
l:
417

@@ -8,11 +21,23 @@ l:
821
x = .-.data
922
.long x
1023
.set x,.-.data
11-
# CHECK: [[#@LINE-1]]:8: error: invalid reassignment of non-absolute variable 'x'
12-
## TODO This should be allowed
1324
.long x
1425

1526
.globl l_v
1627
.set l_v, l
1728
.globl l_v
1829
.set l_v, l
30+
31+
.section .data1,"aw"
32+
.equiv b, 2*a
33+
.set a, 1
34+
.equiv c, 3*a
35+
36+
.if b > a
37+
.byte a, b, c
38+
.endif
39+
40+
.set a, -a
41+
.if b > a
42+
.byte a, b, c
43+
.endif

0 commit comments

Comments
 (0)