[lld] fae9610 - [ELF] Support operator ^ and ^=

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Sat Jul 15 14:10:44 PDT 2023


Author: Fangrui Song
Date: 2023-07-15T14:10:40-07:00
New Revision: fae96104d4378166cbe5c875ef8ed808a356f3fb

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

LOG: [ELF] Support operator ^ and ^=

GNU ld added ^ support in July 2023 and it looks like ^= is in plan as
well.

For now, we don't support `a^=0` (^= without a preceding space).

Added: 
    

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

Removed: 
    


################################################################################
diff  --git a/lld/ELF/ScriptLexer.cpp b/lld/ELF/ScriptLexer.cpp
index a10927666dcdb6..14f39ed10e17c2 100644
--- a/lld/ELF/ScriptLexer.cpp
+++ b/lld/ELF/ScriptLexer.cpp
@@ -140,7 +140,7 @@ void ScriptLexer::tokenize(MemoryBufferRef mb) {
       s = s.substr(3);
       continue;
     }
-    if (s.size() > 1 && ((s[1] == '=' && strchr("*/+-<>&|", s[0])) ||
+    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);
@@ -196,7 +196,7 @@ bool ScriptLexer::atEOF() { return errorCount() || tokens.size() == pos; }
 // Split a given string as an expression.
 // This function returns "3", "*" and "5" for "3*5" for example.
 static std::vector<StringRef> tokenizeExpr(StringRef s) {
-  StringRef ops = "!~*/+-<>?:="; // List of operators
+  StringRef ops = "!~*/+-<>?^:="; // List of operators
 
   // Quoted strings are literal strings, so we don't want to split it.
   if (s.starts_with("\""))

diff  --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index 9f20cefe4c5f42..dbe86beae59b67 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -177,6 +177,12 @@ static ExprValue bitAnd(ExprValue a, ExprValue b) {
           (a.getValue() & b.getValue()) - a.getSecAddr(), a.loc};
 }
 
+static ExprValue bitXor(ExprValue a, ExprValue b) {
+  moveAbsRight(a, b);
+  return {a.sec, a.forceAbsolute,
+          (a.getValue() ^ b.getValue()) - a.getSecAddr(), a.loc};
+}
+
 static ExprValue bitOr(ExprValue a, ExprValue b) {
   moveAbsRight(a, b);
   return {a.sec, a.forceAbsolute,
@@ -638,12 +644,13 @@ void ScriptParser::readTarget() {
 
 static int precedence(StringRef op) {
   return StringSwitch<int>(op)
-      .Cases("*", "/", "%", 10)
-      .Cases("+", "-", 9)
-      .Cases("<<", ">>", 8)
-      .Cases("<", "<=", ">", ">=", 7)
-      .Cases("==", "!=", 6)
-      .Case("&", 5)
+      .Cases("*", "/", "%", 11)
+      .Cases("+", "-", 10)
+      .Cases("<<", ">>", 9)
+      .Cases("<", "<=", ">", ">=", 8)
+      .Cases("==", "!=", 7)
+      .Case("&", 6)
+      .Case("^", 5)
       .Case("|", 4)
       .Case("&&", 3)
       .Case("||", 2)
@@ -1047,7 +1054,7 @@ SymbolAssignment *ScriptParser::readAssignment(StringRef tok) {
     // Support = followed by an expression without whitespace.
     SaveAndRestore saved(inExpr, true);
     cmd = readSymbolAssignment(tok);
-  } else if ((op.size() == 2 && op[1] == '=' && strchr("*/+-&|", op[0])) ||
+  } else if ((op.size() == 2 && op[1] == '=' && strchr("*/+-&^|", op[0])) ||
              op == "<<=" || op == ">>=") {
     cmd = readSymbolAssignment(tok);
   } else if (tok == "PROVIDE") {
@@ -1074,7 +1081,7 @@ SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef name) {
   name = unquote(name);
   StringRef op = next();
   assert(op == "=" || op == "*=" || op == "/=" || op == "+=" || op == "-=" ||
-         op == "&=" || op == "|=" || op == "<<=" || op == ">>=");
+         op == "&=" || op == "^=" || op == "|=" || op == "<<=" || op == ">>=");
   // Note: GNU ld does not support %= or ^=.
   Expr e = readExpr();
   if (op != "=") {
@@ -1099,6 +1106,8 @@ SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef name) {
         return lhs.getValue() >> e().getValue() % 64;
       case '&':
         return lhs.getValue() & e().getValue();
+      case '^':
+        return lhs.getValue() ^ e().getValue();
       case '|':
         return lhs.getValue() | e().getValue();
       default:
@@ -1168,6 +1177,8 @@ Expr ScriptParser::combine(StringRef op, Expr l, Expr r) {
     return [=] { return l().getValue() && r().getValue(); };
   if (op == "&")
     return [=] { return bitAnd(l(), r()); };
+  if (op == "^")
+    return [=] { return bitXor(l(), r()); };
   if (op == "|")
     return [=] { return bitOr(l(), r()); };
   llvm_unreachable("invalid operator");

diff  --git a/lld/test/ELF/linkerscript/diag3.test b/lld/test/ELF/linkerscript/diag3.test
index 1e4371241f304a..1df8d601db0164 100644
--- a/lld/test/ELF/linkerscript/diag3.test
+++ b/lld/test/ELF/linkerscript/diag3.test
@@ -8,6 +8,6 @@ SECTIONS {
   boom ^temp : { *(.temp) }
 }
 
-# CHECK:      8: malformed number: ^temp
+# CHECK:      8: malformed number: ^
 # CHECK-NEXT: >>>   boom ^temp : { *(.temp) }
 # CHECK-NEXT: >>>        ^

diff  --git a/lld/test/ELF/linkerscript/diag4.test b/lld/test/ELF/linkerscript/diag4.test
index 15d81fdc2be4ad..d93a69a95c61dd 100644
--- a/lld/test/ELF/linkerscript/diag4.test
+++ b/lld/test/ELF/linkerscript/diag4.test
@@ -9,6 +9,6 @@ SECTIONS {
   boom ^temp : { *(.temp) }
 }
 
-# CHECK:      9: malformed number: ^temp
+# CHECK:      9: malformed number: ^{{$}}
 # CHECK-NEXT: >>>   boom ^temp : { *(.temp) }
 # CHECK-NEXT: >>>        ^

diff  --git a/lld/test/ELF/linkerscript/diag5.test b/lld/test/ELF/linkerscript/diag5.test
index 15d81fdc2be4ad..9a2304baa44131 100644
--- a/lld/test/ELF/linkerscript/diag5.test
+++ b/lld/test/ELF/linkerscript/diag5.test
@@ -9,6 +9,6 @@ SECTIONS {
   boom ^temp : { *(.temp) }
 }
 
-# CHECK:      9: malformed number: ^temp
+# CHECK:      9: malformed number: ^
 # CHECK-NEXT: >>>   boom ^temp : { *(.temp) }
 # CHECK-NEXT: >>>        ^

diff  --git a/lld/test/ELF/linkerscript/operators.test b/lld/test/ELF/linkerscript/operators.test
index 0b6fdc2fe63f8e..27209a2e40f598 100644
--- a/lld/test/ELF/linkerscript/operators.test
+++ b/lld/test/ELF/linkerscript/operators.test
@@ -21,6 +21,7 @@ SECTIONS {
   neq = 1 != 1 <= 1 ? 1 : 2;
   and = 3 & 4 > 0;
   or = 0xbb & 0xee | 1;
+  xor = 3&3^5|1;
   logicaland = (0 && 0) + (0&&1)*2 + (1&& 0)*4 + (1 &&1) *8;
   logicaland2 = 1 & 0 && 1 | 1;
   logicalor = (0 || 0) + (0||1)*2 + (1|| 0)*4 + (1 ||1) *8;
@@ -44,8 +45,13 @@ SECTIONS {
   rshiftassign >>= 130;  # arbitrarily reduced to 2
   andassign = 6;
   andassign &= 4;
+  andassign&=4;
+  xorassign = 6;
+  xorassign ^= 3;
+  xorassign ^=0;
   orassign = 4;
   orassign |= 1;
+  orassign|=1;
   braces = 1 + (2 + 3) * 4;
   precedence1 = 1|0xff&1/1<<1+1*2;
   precedence2 = (1 | (0xff & (1 << (1 + (1 * 2)))));
@@ -85,6 +91,7 @@ SECTIONS {
 # CHECK-NEXT: 0000000000000002 A neq
 # CHECK-NEXT: 0000000000000001 A and
 # CHECK-NEXT: 00000000000000ab A or
+# CHECK-NEXT: 0000000000000007 A xor
 # CHECK-NEXT: 0000000000000008 A logicaland
 # CHECK-NEXT: 0000000000000000 A logicaland2
 # CHECK-NEXT: 000000000000000e A logicalor
@@ -98,6 +105,7 @@ SECTIONS {
 # CHECK-NEXT: 0000000000000004 A lshiftassign
 # CHECK-NEXT: 0000000000000003 A rshiftassign
 # CHECK-NEXT: 0000000000000004 A andassign
+# CHECK-NEXT: 0000000000000005 A xorassign
 # CHECK-NEXT: 0000000000000005 A orassign
 # CHECK-NEXT: 0000000000000015 A braces
 # CHECK-NEXT: 0000000000000009 A precedence1
@@ -165,10 +173,12 @@ SECTIONS {
 # 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
 
-## GNU ld does not support %= or ^=.
+## GNU ld does not support %=.
 # 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=UNKNOWN %s
-# 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=UNKNOWN %s
+# RUN: not ld.lld %t.o -T %t.script -o /dev/null 2>&1 | FileCheck --check-prefix=UNKNOWN1 %s
+## For now, we don't support ^= without a preceding space.
+# 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=UNKNOWN2 %s
 
-# UNKNOWN: error: {{.*}}:1: unknown directive: a
+# UNKNOWN1: error: {{.*}}:1: unknown directive: a{{$}}
+# UNKNOWN2: error: {{.*}}:1: unknown directive: a^=0{{$}}


        


More information about the llvm-commits mailing list