[llvm] d7282c5 - [llvm-rc] Add support for multiplication and division in expressions (#143373)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 10 13:34:29 PDT 2025
Author: Martin Storsjö
Date: 2025-06-10T23:34:26+03:00
New Revision: d7282c56cd294a2eb4890e50c84e6eae6f7c6671
URL: https://github.com/llvm/llvm-project/commit/d7282c56cd294a2eb4890e50c84e6eae6f7c6671
DIFF: https://github.com/llvm/llvm-project/commit/d7282c56cd294a2eb4890e50c84e6eae6f7c6671.diff
LOG: [llvm-rc] Add support for multiplication and division in expressions (#143373)
This is supported by GNU windres. MS rc.exe does accept these
expressions, but doesn't evalulate them correctly, it only returns the
left hand side.
This fixes one aspect of
https://github.com/llvm/llvm-project/issues/143157.
Added:
Modified:
llvm/test/tools/llvm-rc/Inputs/parser-expr.rc
llvm/test/tools/llvm-rc/Inputs/tokens.rc
llvm/test/tools/llvm-rc/parser-expr.test
llvm/test/tools/llvm-rc/tokenizer.test
llvm/tools/llvm-rc/ResourceScriptParser.cpp
llvm/tools/llvm-rc/ResourceScriptParser.h
llvm/tools/llvm-rc/ResourceScriptStmt.h
llvm/tools/llvm-rc/ResourceScriptToken.cpp
llvm/tools/llvm-rc/ResourceScriptToken.h
llvm/tools/llvm-rc/ResourceScriptTokenList.def
Removed:
################################################################################
diff --git a/llvm/test/tools/llvm-rc/Inputs/parser-expr.rc b/llvm/test/tools/llvm-rc/Inputs/parser-expr.rc
index 8e69c1cd1fa16..2f8e4b2d344a0 100644
--- a/llvm/test/tools/llvm-rc/Inputs/parser-expr.rc
+++ b/llvm/test/tools/llvm-rc/Inputs/parser-expr.rc
@@ -5,6 +5,11 @@ LANGUAGE 1|1&0, 0&0|1
LANGUAGE 3+4-5, 3-4+5
LANGUAGE 1+2|3, 3|1+2
LANGUAGE 6&~5, 6&-8
+LANGUAGE 7/3, 7*3
+LANGUAGE 5/2*2, 5*3/2
+LANGUAGE 1+2*3, (1+2)*3
+LANGUAGE 100/12/5*5, 1+1+1+1*4
+LANGUAGE 9/(1+3), (4+5)/4
LANGUAGE -1, --1
LANGUAGE ----1, -----1
LANGUAGE ~1, ~~1
diff --git a/llvm/test/tools/llvm-rc/Inputs/tokens.rc b/llvm/test/tools/llvm-rc/Inputs/tokens.rc
index 6a781202a7e37..20f77912477d9 100644
--- a/llvm/test/tools/llvm-rc/Inputs/tokens.rc
+++ b/llvm/test/tools/llvm-rc/Inputs/tokens.rc
@@ -1,4 +1,5 @@
1 + 2 - 3214L & 0x120894 032173 2|&~+(-7){0xabcdef 0xABCDEFl} Begin End
+1*3/4
He11o LLVM
identifier-with-dashes
diff --git a/llvm/test/tools/llvm-rc/parser-expr.test b/llvm/test/tools/llvm-rc/parser-expr.test
index ed6796529fdfa..14a299c9e3e96 100644
--- a/llvm/test/tools/llvm-rc/parser-expr.test
+++ b/llvm/test/tools/llvm-rc/parser-expr.test
@@ -7,6 +7,11 @@
; CHECK-NEXT: Language: 2, Sublanguage: 4
; CHECK-NEXT: Language: 3, Sublanguage: 5
; CHECK-NEXT: Language: 2, Sublanguage: 0
+; CHECK-NEXT: Language: 2, Sublanguage: 21
+; CHECK-NEXT: Language: 4, Sublanguage: 7
+; CHECK-NEXT: Language: 7, Sublanguage: 9
+; CHECK-NEXT: Language: 5, Sublanguage: 7
+; CHECK-NEXT: Language: 2, Sublanguage: 2
; CHECK-NEXT: Language: 4294967295, Sublanguage: 1
; CHECK-NEXT: Language: 1, Sublanguage: 4294967295
; CHECK-NEXT: Language: 4294967294, Sublanguage: 1
diff --git a/llvm/test/tools/llvm-rc/tokenizer.test b/llvm/test/tools/llvm-rc/tokenizer.test
index 8486f8bd78690..3062e2bf64629 100644
--- a/llvm/test/tools/llvm-rc/tokenizer.test
+++ b/llvm/test/tools/llvm-rc/tokenizer.test
@@ -25,6 +25,11 @@
; CHECK-NEXT: BlockEnd: }
; CHECK-NEXT: BlockBegin: Begin
; CHECK-NEXT: BlockEnd: End
+; CHECK-NEXT: Int: 1; int value = 1
+; CHECK-NEXT: Asterisk: *
+; CHECK-NEXT: Int: 3; int value = 3
+; CHECK-NEXT: Slash: /
+; CHECK-NEXT: Int: 4; int value = 4
; CHECK-NEXT: Identifier: He11o
; CHECK-NEXT: Identifier: LLVM
; CHECK-NEXT: Identifier: identifier-with-dashes
diff --git a/llvm/tools/llvm-rc/ResourceScriptParser.cpp b/llvm/tools/llvm-rc/ResourceScriptParser.cpp
index 69798152c1f25..e4efc83c933b4 100644
--- a/llvm/tools/llvm-rc/ResourceScriptParser.cpp
+++ b/llvm/tools/llvm-rc/ResourceScriptParser.cpp
@@ -132,12 +132,13 @@ void RCParser::consume() {
//
// The following grammar is used to parse the expressions Exp1:
// Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
-// Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
-// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
-// separated by binary operators.)
+// Exp2 ::= Exp3 || Exp3 * Exp3 || Exp3 / Exp3
+// Exp3 ::= -Exp3 || ~Exp3 || not Expr3 || Int || (Exp1)
+// (More conveniently, Exp1 and Exp2 are non-empty sequences of Exp3
+// expressions, separated by binary operators.)
//
-// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
-// is read by parseIntExpr2().
+// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, Exp2
+// is read by parseIntExpr2() and Exp3 is read by parseIntExpr3().
//
// The original Microsoft tool handles multiple unary operators incorrectly.
// For example, in 16-bit little-endian integers:
@@ -158,7 +159,7 @@ Expected<IntWithNotMask> RCParser::parseIntExpr1() {
ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
IntWithNotMask Result = *FirstResult;
- while (!isEof() && look().isBinaryOp()) {
+ while (!isEof() && look().isLowPrecedenceBinaryOp()) {
auto OpToken = read();
ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
@@ -180,7 +181,7 @@ Expected<IntWithNotMask> RCParser::parseIntExpr1() {
break;
default:
- llvm_unreachable("Already processed all binary ops.");
+ llvm_unreachable("Already processed all low precedence binary ops.");
}
}
@@ -188,7 +189,33 @@ Expected<IntWithNotMask> RCParser::parseIntExpr1() {
}
Expected<IntWithNotMask> RCParser::parseIntExpr2() {
- // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
+ // Exp2 ::= Exp3 || Exp3 * Exp3 || Exp3 / Exp3.
+ ASSIGN_OR_RETURN(FirstResult, parseIntExpr3());
+ IntWithNotMask Result = *FirstResult;
+
+ while (!isEof() && look().isHighPrecedenceBinaryOp()) {
+ auto OpToken = read();
+ ASSIGN_OR_RETURN(NextResult, parseIntExpr3());
+
+ switch (OpToken.kind()) {
+ case Kind::Asterisk:
+ Result *= *NextResult;
+ break;
+
+ case Kind::Slash:
+ Result /= *NextResult;
+ break;
+
+ default:
+ llvm_unreachable("Already processed all high precedence binary ops.");
+ }
+ }
+
+ return Result;
+}
+
+Expected<IntWithNotMask> RCParser::parseIntExpr3() {
+ // Exp3 ::= -Exp3 || ~Exp3 || not Expr3 || Int || (Exp1).
static const char ErrorMsg[] = "'-', '~', integer or '('";
if (isEof())
@@ -197,13 +224,13 @@ Expected<IntWithNotMask> RCParser::parseIntExpr2() {
switch (look().kind()) {
case Kind::Minus: {
consume();
- ASSIGN_OR_RETURN(Result, parseIntExpr2());
+ ASSIGN_OR_RETURN(Result, parseIntExpr3());
return -(*Result);
}
case Kind::Tilde: {
consume();
- ASSIGN_OR_RETURN(Result, parseIntExpr2());
+ ASSIGN_OR_RETURN(Result, parseIntExpr3());
return ~(*Result);
}
@@ -220,7 +247,7 @@ Expected<IntWithNotMask> RCParser::parseIntExpr2() {
case Kind::Identifier: {
if (!read().value().equals_insensitive("not"))
return getExpectedError(ErrorMsg, true);
- ASSIGN_OR_RETURN(Result, parseIntExpr2());
+ ASSIGN_OR_RETURN(Result, parseIntExpr3());
return IntWithNotMask(0, (*Result).getValue());
}
diff --git a/llvm/tools/llvm-rc/ResourceScriptParser.h b/llvm/tools/llvm-rc/ResourceScriptParser.h
index aa7f847187c49..1e7618c84142e 100644
--- a/llvm/tools/llvm-rc/ResourceScriptParser.h
+++ b/llvm/tools/llvm-rc/ResourceScriptParser.h
@@ -88,6 +88,7 @@ class RCParser {
// Helper integer expression parsing methods.
Expected<IntWithNotMask> parseIntExpr1();
Expected<IntWithNotMask> parseIntExpr2();
+ Expected<IntWithNotMask> parseIntExpr3();
// Advance the state by one, discarding the current token.
// If the discarded token had an incorrect type, fail.
diff --git a/llvm/tools/llvm-rc/ResourceScriptStmt.h b/llvm/tools/llvm-rc/ResourceScriptStmt.h
index 8f099202c0b47..a81e384fda365 100644
--- a/llvm/tools/llvm-rc/ResourceScriptStmt.h
+++ b/llvm/tools/llvm-rc/ResourceScriptStmt.h
@@ -49,6 +49,16 @@ class RCInt {
return *this;
}
+ RCInt &operator*=(const RCInt &Rhs) {
+ std::tie(Val, Long) = std::make_pair(Val * Rhs.Val, Long | Rhs.Long);
+ return *this;
+ }
+
+ RCInt &operator/=(const RCInt &Rhs) {
+ std::tie(Val, Long) = std::make_pair(Val / Rhs.Val, Long | Rhs.Long);
+ return *this;
+ }
+
RCInt &operator|=(const RCInt &Rhs) {
std::tie(Val, Long) = std::make_pair(Val | Rhs.Val, Long | Rhs.Long);
return *this;
@@ -98,6 +108,20 @@ class IntWithNotMask {
return *this;
}
+ IntWithNotMask &operator*=(const IntWithNotMask &Rhs) {
+ Value &= ~Rhs.NotMask;
+ Value *= Rhs.Value;
+ NotMask |= Rhs.NotMask;
+ return *this;
+ }
+
+ IntWithNotMask &operator/=(const IntWithNotMask &Rhs) {
+ Value &= ~Rhs.NotMask;
+ Value /= Rhs.Value;
+ NotMask |= Rhs.NotMask;
+ return *this;
+ }
+
IntWithNotMask &operator|=(const IntWithNotMask &Rhs) {
Value &= ~Rhs.NotMask;
Value |= Rhs.Value;
diff --git a/llvm/tools/llvm-rc/ResourceScriptToken.cpp b/llvm/tools/llvm-rc/ResourceScriptToken.cpp
index aad1060c4a381..0070037e63e6a 100644
--- a/llvm/tools/llvm-rc/ResourceScriptToken.cpp
+++ b/llvm/tools/llvm-rc/ResourceScriptToken.cpp
@@ -64,7 +64,7 @@ StringRef RCToken::value() const { return TokenValue; }
Kind RCToken::kind() const { return TokenKind; }
-bool RCToken::isBinaryOp() const {
+bool RCToken::isLowPrecedenceBinaryOp() const {
switch (TokenKind) {
case Kind::Plus:
case Kind::Minus:
@@ -76,6 +76,16 @@ bool RCToken::isBinaryOp() const {
}
}
+bool RCToken::isHighPrecedenceBinaryOp() const {
+ switch (TokenKind) {
+ case Kind::Asterisk:
+ case Kind::Slash:
+ return true;
+ default:
+ return false;
+ }
+}
+
static Error getStringError(const Twine &message) {
return make_error<StringError>("Error parsing file: " + message,
inconvertibleErrorCode());
diff --git a/llvm/tools/llvm-rc/ResourceScriptToken.h b/llvm/tools/llvm-rc/ResourceScriptToken.h
index 29f7502f89efd..3dcdfafd2d576 100644
--- a/llvm/tools/llvm-rc/ResourceScriptToken.h
+++ b/llvm/tools/llvm-rc/ResourceScriptToken.h
@@ -56,8 +56,11 @@ class RCToken {
StringRef value() const;
Kind kind() const;
- // Check if a token describes a binary operator.
- bool isBinaryOp() const;
+ // Check if a token describes a low precedence binary operator.
+ bool isLowPrecedenceBinaryOp() const;
+
+ // Check if a token describes a high precedence binary operator.
+ bool isHighPrecedenceBinaryOp() const;
private:
Kind TokenKind;
diff --git a/llvm/tools/llvm-rc/ResourceScriptTokenList.def b/llvm/tools/llvm-rc/ResourceScriptTokenList.def
index a61a96461f0fb..6ee13b2815d35 100644
--- a/llvm/tools/llvm-rc/ResourceScriptTokenList.def
+++ b/llvm/tools/llvm-rc/ResourceScriptTokenList.def
@@ -29,6 +29,8 @@ SHORT_TOKEN(BlockEnd, '}') // End of the block; can also be END.
SHORT_TOKEN(Comma, ',') // Comma - resource arguments separator.
SHORT_TOKEN(Plus, '+') // Addition operator.
SHORT_TOKEN(Minus, '-') // Subtraction operator.
+SHORT_TOKEN(Asterisk, '*') // Multiplication operator.
+SHORT_TOKEN(Slash, '/') // Division operator.
SHORT_TOKEN(Pipe, '|') // Bitwise-OR operator.
SHORT_TOKEN(Amp, '&') // Bitwise-AND operator.
SHORT_TOKEN(Tilde, '~') // Bitwise-NOT operator.
More information about the llvm-commits
mailing list