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

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Sat Jun 25 22:23:09 PDT 2022


Author: Fangrui Song
Date: 2022-06-25T22:22:59-07:00
New Revision: 0a0effdd5b654ed90f3c3cfae5d9d9dd0a0db8de

URL: https://github.com/llvm/llvm-project/commit/0a0effdd5b654ed90f3c3cfae5d9d9dd0a0db8de
DIFF: https://github.com/llvm/llvm-project/commit/0a0effdd5b654ed90f3c3cfae5d9d9dd0a0db8de.diff

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

Added: 
    

Modified: 
    lld/ELF/ScriptLexer.cpp
    lld/ELF/ScriptParser.cpp
    lld/test/ELF/linkerscript/operators.test

Removed: 
    


################################################################################
diff  --git a/lld/ELF/ScriptLexer.cpp b/lld/ELF/ScriptLexer.cpp
index 6f9ffb790eec..e0a8ed7121a8 100644
--- a/lld/ELF/ScriptLexer.cpp
+++ b/lld/ELF/ScriptLexer.cpp
@@ -134,10 +134,14 @@ void ScriptLexer::tokenize(MemoryBufferRef mb) {
       continue;
     }
 
-    // ">foo" is parsed to ">" and "foo", but ">>" is parsed to ">>".
-    // "|", "||", "&" and "&&" are 
diff erent 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;

diff  --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index 1f0600572bda..b3cc47990552 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -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());
 }

diff  --git a/lld/test/ELF/linkerscript/operators.test b/lld/test/ELF/linkerscript/operators.test
index 2480dad4af46..9ca13c66970a 100644
--- a/lld/test/ELF/linkerscript/operators.test
+++ b/lld/test/ELF/linkerscript/operators.test
@@ -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


        


More information about the llvm-commits mailing list