[ELF] Support -= *= /= <<= >>= &= |= in symbol assignments

This commit is contained in:
Fangrui Song 2022-06-25 22:22:59 -07:00
parent a7938c74f1
commit 0a0effdd5b
3 changed files with 68 additions and 9 deletions

View File

@ -134,10 +134,14 @@ void ScriptLexer::tokenize(MemoryBufferRef mb) {
continue;
}
// ">foo" is parsed to ">" and "foo", but ">>" is parsed to ">>".
// "|", "||", "&" and "&&" are different operators.
if (s.startswith("<<") || s.startswith("<=") || s.startswith(">>") ||
s.startswith(">=") || s.startswith("||") || s.startswith("&&")) {
// Some operators form separate tokens.
if (s.startswith("<<=") || s.startswith(">>=")) {
vec.push_back(s.substr(0, 3));
s = s.substr(3);
continue;
}
if (s.size() > 1 && ((s[1] == '=' && strchr("*/+-<>&|", s[0])) ||
(s[0] == s[1] && strchr("<>&|", s[0])))) {
vec.push_back(s.substr(0, 2));
s = s.substr(2);
continue;

View File

@ -1038,11 +1038,14 @@ SymbolAssignment *ScriptParser::readAssignment(StringRef tok) {
size_t oldPos = pos;
SymbolAssignment *cmd = nullptr;
if (peek().startswith("=")) {
const StringRef op = peek();
if (op.startswith("=")) {
// Support = followed by an expression without whitespace.
SaveAndRestore<bool> saved(inExpr, true);
cmd = readSymbolAssignment(tok);
} else if (peek() == "+=") {
} else if ((op.size() == 2 && op[1] == '=' &&
is_contained("*/+-&|", op[0])) ||
op == "<<=" || op == ">>=") {
cmd = readSymbolAssignment(tok);
} else if (tok == "PROVIDE") {
SaveAndRestore<bool> saved(inExpr, true);
@ -1067,11 +1070,38 @@ SymbolAssignment *ScriptParser::readAssignment(StringRef tok) {
SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef name) {
name = unquote(name);
StringRef op = next();
assert(op == "=" || op == "+=");
assert(op == "=" || op == "*=" || op == "/=" || op == "+=" || op == "-=" ||
op == "&=" || op == "|=" || op == "<<=" || op == ">>=");
// Note: GNU ld does not support %= or ^=.
Expr e = readExpr();
if (op == "+=") {
if (op != "=") {
std::string loc = getCurrentLocation();
e = [=] { return add(script->getSymbolValue(name, loc), e()); };
e = [=, c = op[0]]() -> ExprValue {
ExprValue lhs = script->getSymbolValue(name, loc);
switch (c) {
case '*':
return lhs.getValue() * e().getValue();
case '/':
if (uint64_t rv = e().getValue())
return lhs.getValue() / rv;
error(loc + ": division by zero");
return 0;
case '+':
return add(lhs, e());
case '-':
return sub(lhs, e());
case '<':
return lhs.getValue() << e().getValue();
case '>':
return lhs.getValue() >> e().getValue();
case '&':
return lhs.getValue() & e().getValue();
case '|':
return lhs.getValue() | e().getValue();
default:
llvm_unreachable("");
}
};
}
return make<SymbolAssignment>(name, e, getCurrentLocation());
}

View File

@ -27,8 +27,22 @@ SECTIONS {
ternary1 = 0 ? 1 : 2 & 6;
ternary2 = 1 ? 2?3:4 : 5?6 :7;
mulassign =2;
mulassign *= 2;
divassign = 8;
divassign /= 2;
plusassign =1;
plusassign += 2;
minusassign = 3;
minusassign -= 1;
lshiftassign = 1;
lshiftassign <<= 2;
rshiftassign = 5;
rshiftassign >>= 1;
andassign = 6;
andassign &= 4;
orassign = 4;
orassign |= 1;
braces = 1 + (2 + 3) * 4;
precedence1 = 1|0xff&1/1<<1+1*2;
precedence2 = (1 | (0xff & (1 << (1 + (1 * 2)))));
@ -73,7 +87,14 @@ SECTIONS {
# CHECK-NEXT: 0000000000000001 A logicalor2
# CHECK-NEXT: 0000000000000002 A ternary1
# CHECK-NEXT: 0000000000000003 A ternary2
# CHECK-NEXT: 0000000000000004 A mulassign
# CHECK-NEXT: 0000000000000004 A divassign
# CHECK-NEXT: 0000000000000003 A plusassign
# CHECK-NEXT: 0000000000000002 A minusassign
# CHECK-NEXT: 0000000000000004 A lshiftassign
# CHECK-NEXT: 0000000000000002 A rshiftassign
# CHECK-NEXT: 0000000000000004 A andassign
# CHECK-NEXT: 0000000000000005 A orassign
# CHECK-NEXT: 0000000000000015 A braces
# CHECK-NEXT: 0000000000000009 A precedence1
# CHECK-NEXT: 0000000000000009 A precedence2
@ -135,3 +156,7 @@ SECTIONS {
# RUN: not ld.lld %t.o -T %t.script -o /dev/null 2>&1 | \
# RUN: FileCheck --check-prefix=TERNERR %s
# TERNERR: : expected, but got ;
## Div by zero error.
# RUN: echo 'a = 1; a /= 0;' > %t.script
# RUN: not ld.lld %t.o -T %t.script -o /dev/null 2>&1 | FileCheck --check-prefix=DIVZERO %s