Skip to content

Commit d7282c5

Browse files
authored
[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 #143157.
1 parent a7f495f commit d7282c5

File tree

10 files changed

+97
-14
lines changed

10 files changed

+97
-14
lines changed

llvm/test/tools/llvm-rc/Inputs/parser-expr.rc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ LANGUAGE 1|1&0, 0&0|1
55
LANGUAGE 3+4-5, 3-4+5
66
LANGUAGE 1+2|3, 3|1+2
77
LANGUAGE 6&~5, 6&-8
8+
LANGUAGE 7/3, 7*3
9+
LANGUAGE 5/2*2, 5*3/2
10+
LANGUAGE 1+2*3, (1+2)*3
11+
LANGUAGE 100/12/5*5, 1+1+1+1*4
12+
LANGUAGE 9/(1+3), (4+5)/4
813
LANGUAGE -1, --1
914
LANGUAGE ----1, -----1
1015
LANGUAGE ~1, ~~1

llvm/test/tools/llvm-rc/Inputs/tokens.rc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
1 + 2 - 3214L & 0x120894 032173 2|&~+(-7){0xabcdef 0xABCDEFl} Begin End
2+
1*3/4
23
He11o LLVM
34
identifier-with-dashes
45

llvm/test/tools/llvm-rc/parser-expr.test

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
; CHECK-NEXT: Language: 2, Sublanguage: 4
88
; CHECK-NEXT: Language: 3, Sublanguage: 5
99
; CHECK-NEXT: Language: 2, Sublanguage: 0
10+
; CHECK-NEXT: Language: 2, Sublanguage: 21
11+
; CHECK-NEXT: Language: 4, Sublanguage: 7
12+
; CHECK-NEXT: Language: 7, Sublanguage: 9
13+
; CHECK-NEXT: Language: 5, Sublanguage: 7
14+
; CHECK-NEXT: Language: 2, Sublanguage: 2
1015
; CHECK-NEXT: Language: 4294967295, Sublanguage: 1
1116
; CHECK-NEXT: Language: 1, Sublanguage: 4294967295
1217
; CHECK-NEXT: Language: 4294967294, Sublanguage: 1

llvm/test/tools/llvm-rc/tokenizer.test

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
; CHECK-NEXT: BlockEnd: }
2626
; CHECK-NEXT: BlockBegin: Begin
2727
; CHECK-NEXT: BlockEnd: End
28+
; CHECK-NEXT: Int: 1; int value = 1
29+
; CHECK-NEXT: Asterisk: *
30+
; CHECK-NEXT: Int: 3; int value = 3
31+
; CHECK-NEXT: Slash: /
32+
; CHECK-NEXT: Int: 4; int value = 4
2833
; CHECK-NEXT: Identifier: He11o
2934
; CHECK-NEXT: Identifier: LLVM
3035
; CHECK-NEXT: Identifier: identifier-with-dashes

llvm/tools/llvm-rc/ResourceScriptParser.cpp

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,13 @@ void RCParser::consume() {
132132
//
133133
// The following grammar is used to parse the expressions Exp1:
134134
// Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
135-
// Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
136-
// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
137-
// separated by binary operators.)
135+
// Exp2 ::= Exp3 || Exp3 * Exp3 || Exp3 / Exp3
136+
// Exp3 ::= -Exp3 || ~Exp3 || not Expr3 || Int || (Exp1)
137+
// (More conveniently, Exp1 and Exp2 are non-empty sequences of Exp3
138+
// expressions, separated by binary operators.)
138139
//
139-
// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
140-
// is read by parseIntExpr2().
140+
// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, Exp2
141+
// is read by parseIntExpr2() and Exp3 is read by parseIntExpr3().
141142
//
142143
// The original Microsoft tool handles multiple unary operators incorrectly.
143144
// For example, in 16-bit little-endian integers:
@@ -158,7 +159,7 @@ Expected<IntWithNotMask> RCParser::parseIntExpr1() {
158159
ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
159160
IntWithNotMask Result = *FirstResult;
160161

161-
while (!isEof() && look().isBinaryOp()) {
162+
while (!isEof() && look().isLowPrecedenceBinaryOp()) {
162163
auto OpToken = read();
163164
ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
164165

@@ -180,15 +181,41 @@ Expected<IntWithNotMask> RCParser::parseIntExpr1() {
180181
break;
181182

182183
default:
183-
llvm_unreachable("Already processed all binary ops.");
184+
llvm_unreachable("Already processed all low precedence binary ops.");
184185
}
185186
}
186187

187188
return Result;
188189
}
189190

190191
Expected<IntWithNotMask> RCParser::parseIntExpr2() {
191-
// Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
192+
// Exp2 ::= Exp3 || Exp3 * Exp3 || Exp3 / Exp3.
193+
ASSIGN_OR_RETURN(FirstResult, parseIntExpr3());
194+
IntWithNotMask Result = *FirstResult;
195+
196+
while (!isEof() && look().isHighPrecedenceBinaryOp()) {
197+
auto OpToken = read();
198+
ASSIGN_OR_RETURN(NextResult, parseIntExpr3());
199+
200+
switch (OpToken.kind()) {
201+
case Kind::Asterisk:
202+
Result *= *NextResult;
203+
break;
204+
205+
case Kind::Slash:
206+
Result /= *NextResult;
207+
break;
208+
209+
default:
210+
llvm_unreachable("Already processed all high precedence binary ops.");
211+
}
212+
}
213+
214+
return Result;
215+
}
216+
217+
Expected<IntWithNotMask> RCParser::parseIntExpr3() {
218+
// Exp3 ::= -Exp3 || ~Exp3 || not Expr3 || Int || (Exp1).
192219
static const char ErrorMsg[] = "'-', '~', integer or '('";
193220

194221
if (isEof())
@@ -197,13 +224,13 @@ Expected<IntWithNotMask> RCParser::parseIntExpr2() {
197224
switch (look().kind()) {
198225
case Kind::Minus: {
199226
consume();
200-
ASSIGN_OR_RETURN(Result, parseIntExpr2());
227+
ASSIGN_OR_RETURN(Result, parseIntExpr3());
201228
return -(*Result);
202229
}
203230

204231
case Kind::Tilde: {
205232
consume();
206-
ASSIGN_OR_RETURN(Result, parseIntExpr2());
233+
ASSIGN_OR_RETURN(Result, parseIntExpr3());
207234
return ~(*Result);
208235
}
209236

@@ -220,7 +247,7 @@ Expected<IntWithNotMask> RCParser::parseIntExpr2() {
220247
case Kind::Identifier: {
221248
if (!read().value().equals_insensitive("not"))
222249
return getExpectedError(ErrorMsg, true);
223-
ASSIGN_OR_RETURN(Result, parseIntExpr2());
250+
ASSIGN_OR_RETURN(Result, parseIntExpr3());
224251
return IntWithNotMask(0, (*Result).getValue());
225252
}
226253

llvm/tools/llvm-rc/ResourceScriptParser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class RCParser {
8888
// Helper integer expression parsing methods.
8989
Expected<IntWithNotMask> parseIntExpr1();
9090
Expected<IntWithNotMask> parseIntExpr2();
91+
Expected<IntWithNotMask> parseIntExpr3();
9192

9293
// Advance the state by one, discarding the current token.
9394
// If the discarded token had an incorrect type, fail.

llvm/tools/llvm-rc/ResourceScriptStmt.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@ class RCInt {
4949
return *this;
5050
}
5151

52+
RCInt &operator*=(const RCInt &Rhs) {
53+
std::tie(Val, Long) = std::make_pair(Val * Rhs.Val, Long | Rhs.Long);
54+
return *this;
55+
}
56+
57+
RCInt &operator/=(const RCInt &Rhs) {
58+
std::tie(Val, Long) = std::make_pair(Val / Rhs.Val, Long | Rhs.Long);
59+
return *this;
60+
}
61+
5262
RCInt &operator|=(const RCInt &Rhs) {
5363
std::tie(Val, Long) = std::make_pair(Val | Rhs.Val, Long | Rhs.Long);
5464
return *this;
@@ -98,6 +108,20 @@ class IntWithNotMask {
98108
return *this;
99109
}
100110

111+
IntWithNotMask &operator*=(const IntWithNotMask &Rhs) {
112+
Value &= ~Rhs.NotMask;
113+
Value *= Rhs.Value;
114+
NotMask |= Rhs.NotMask;
115+
return *this;
116+
}
117+
118+
IntWithNotMask &operator/=(const IntWithNotMask &Rhs) {
119+
Value &= ~Rhs.NotMask;
120+
Value /= Rhs.Value;
121+
NotMask |= Rhs.NotMask;
122+
return *this;
123+
}
124+
101125
IntWithNotMask &operator|=(const IntWithNotMask &Rhs) {
102126
Value &= ~Rhs.NotMask;
103127
Value |= Rhs.Value;

llvm/tools/llvm-rc/ResourceScriptToken.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ StringRef RCToken::value() const { return TokenValue; }
6464

6565
Kind RCToken::kind() const { return TokenKind; }
6666

67-
bool RCToken::isBinaryOp() const {
67+
bool RCToken::isLowPrecedenceBinaryOp() const {
6868
switch (TokenKind) {
6969
case Kind::Plus:
7070
case Kind::Minus:
@@ -76,6 +76,16 @@ bool RCToken::isBinaryOp() const {
7676
}
7777
}
7878

79+
bool RCToken::isHighPrecedenceBinaryOp() const {
80+
switch (TokenKind) {
81+
case Kind::Asterisk:
82+
case Kind::Slash:
83+
return true;
84+
default:
85+
return false;
86+
}
87+
}
88+
7989
static Error getStringError(const Twine &message) {
8090
return make_error<StringError>("Error parsing file: " + message,
8191
inconvertibleErrorCode());

llvm/tools/llvm-rc/ResourceScriptToken.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,11 @@ class RCToken {
5656
StringRef value() const;
5757
Kind kind() const;
5858

59-
// Check if a token describes a binary operator.
60-
bool isBinaryOp() const;
59+
// Check if a token describes a low precedence binary operator.
60+
bool isLowPrecedenceBinaryOp() const;
61+
62+
// Check if a token describes a high precedence binary operator.
63+
bool isHighPrecedenceBinaryOp() const;
6164

6265
private:
6366
Kind TokenKind;

llvm/tools/llvm-rc/ResourceScriptTokenList.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ SHORT_TOKEN(BlockEnd, '}') // End of the block; can also be END.
2929
SHORT_TOKEN(Comma, ',') // Comma - resource arguments separator.
3030
SHORT_TOKEN(Plus, '+') // Addition operator.
3131
SHORT_TOKEN(Minus, '-') // Subtraction operator.
32+
SHORT_TOKEN(Asterisk, '*') // Multiplication operator.
33+
SHORT_TOKEN(Slash, '/') // Division operator.
3234
SHORT_TOKEN(Pipe, '|') // Bitwise-OR operator.
3335
SHORT_TOKEN(Amp, '&') // Bitwise-AND operator.
3436
SHORT_TOKEN(Tilde, '~') // Bitwise-NOT operator.

0 commit comments

Comments
 (0)