34fa3c1fb5
All other moves generated by the register allocator to satisfy
constraints were using `PrefersRegister` for the source, but
`SameAsFirstInput` was using `Any`. This lead to situations
where hot code inside a loop would repeatedly reload a constant.
To avoid these situations switch `SameAsFirstInput` to use
`PrefersRegister` when the use occurs inside a loop.
Additionally introduce an extension of `SameAsFirstInput`:
`SameAsFirstOrSecondInput`. This new constraint allows register
allocator to reorder first and second inputs if the second one
is no longer alive after the instruction. This allows register
allocator to avoid unnecessary move.
`SameAsFirstOrSecondInput` can be used as an output constraint
for commutative binary operations on X64
Additionally this CL adds a nascent infrastructure for writing
unit tests against register allocator. See `linearscan_test.cc`.
This CL improves code quality for tight loops with binary
double operations written with constants on the left, for example:
loop {
doubleA = C * doubleB
}
Before this CL `C` would be reloaded into a register immediately
before multiplication, but after this CL it will be kept in
register (if register pressure allows).
Issue https://github.com/dart-lang/sdk/issues/56705
TEST=LinearScan_TestSameAsFirstOrSecond*
Change-Id: Id8e8242a8d1c1d1b8958076f10257e21e4a00aae
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/385001
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Slava Egorov <vegorov@google.com>
403 lines
20 KiB
C++
403 lines
20 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_TOKEN_H_
|
|
#define RUNTIME_VM_TOKEN_H_
|
|
|
|
#include "platform/assert.h"
|
|
#include "vm/allocation.h"
|
|
|
|
namespace dart {
|
|
|
|
// Operator precedence table
|
|
//
|
|
// 14 multiplicative * / ~/ %
|
|
// 13 additive + -
|
|
// 12 shift << >> >>>
|
|
// 11 bitwise and &
|
|
// 10 bitwise xor ^
|
|
// 9 bitwise or |
|
|
// 8 relational >= > <= < is as
|
|
// 7 equality == != === !==
|
|
// 6 logical and &&
|
|
// 5 logical or ||
|
|
// 4 null check ??
|
|
// 3 conditional ?
|
|
// 2 assignment = *= /= ~/= %= += -= <<= >>= >>>= &= ^= |= ??=
|
|
// 1 comma ,
|
|
|
|
// Token definitions.
|
|
// Some operator tokens appear in blocks, e.g. assignment operators.
|
|
// There is code that depends on the values within a block to be
|
|
// contiguous, and on the order of values.
|
|
#define DART_TOKEN_LIST(TOK) \
|
|
TOK(kEOS, "", 0, kNoAttribute) \
|
|
\
|
|
TOK(kLPAREN, "(", 0, kNoAttribute) \
|
|
TOK(kRPAREN, ")", 0, kNoAttribute) \
|
|
TOK(kLBRACK, "[", 0, kNoAttribute) \
|
|
TOK(kRBRACK, "]", 0, kNoAttribute) \
|
|
TOK(kLBRACE, "{", 0, kNoAttribute) \
|
|
TOK(kRBRACE, "}", 0, kNoAttribute) \
|
|
TOK(kARROW, "=>", 0, kNoAttribute) \
|
|
TOK(kCOLON, ":", 0, kNoAttribute) \
|
|
TOK(kSEMICOLON, ";", 0, kNoAttribute) \
|
|
TOK(kPERIOD, ".", 0, kNoAttribute) \
|
|
TOK(kQM_PERIOD, "?.", 0, kNoAttribute) \
|
|
TOK(kINCR, "++", 0, kNoAttribute) \
|
|
TOK(kDECR, "--", 0, kNoAttribute) \
|
|
\
|
|
/* Assignment operators. */ \
|
|
/* Please update IsAssignmentOperator() if you make */ \
|
|
/* any changes to this block. */ \
|
|
TOK(kASSIGN, "=", 2, kNoAttribute) \
|
|
TOK(kASSIGN_OR, "|=", 2, kNoAttribute) \
|
|
TOK(kASSIGN_XOR, "^=", 2, kNoAttribute) \
|
|
TOK(kASSIGN_AND, "&=", 2, kNoAttribute) \
|
|
TOK(kASSIGN_SHL, "<<=", 2, kNoAttribute) \
|
|
TOK(kASSIGN_SHR, ">>=", 2, kNoAttribute) \
|
|
TOK(kASSIGN_USHR, ">>>=", 2, kNoAttribute) \
|
|
TOK(kASSIGN_ADD, "+=", 2, kNoAttribute) \
|
|
TOK(kASSIGN_SUB, "-=", 2, kNoAttribute) \
|
|
TOK(kASSIGN_MUL, "*=", 2, kNoAttribute) \
|
|
TOK(kASSIGN_TRUNCDIV, "~/=", 2, kNoAttribute) \
|
|
TOK(kASSIGN_DIV, "/=", 2, kNoAttribute) \
|
|
TOK(kASSIGN_MOD, "%=", 2, kNoAttribute) \
|
|
/* Avoid trigraph ??= below. */ \
|
|
TOK(kASSIGN_COND, "?\?=", 2, kNoAttribute) \
|
|
\
|
|
TOK(kCASCADE, "..", 2, kNoAttribute) \
|
|
\
|
|
TOK(kCOMMA, ",", 1, kNoAttribute) \
|
|
TOK(kOR, "||", 5, kNoAttribute) \
|
|
TOK(kAND, "&&", 6, kNoAttribute) \
|
|
TOK(kBIT_OR, "|", 9, kNoAttribute) \
|
|
TOK(kBIT_XOR, "^", 10, kNoAttribute) \
|
|
TOK(kBIT_AND, "&", 11, kNoAttribute) \
|
|
TOK(kBIT_NOT, "~", 0, kNoAttribute) \
|
|
\
|
|
/* Shift operators. */ \
|
|
TOK(kSHL, "<<", 12, kNoAttribute) \
|
|
TOK(kSHR, ">>", 12, kNoAttribute) \
|
|
TOK(kUSHR, ">>>", 12, kNoAttribute) \
|
|
\
|
|
/* Additive operators. */ \
|
|
TOK(kADD, "+", 13, kNoAttribute) \
|
|
TOK(kSUB, "-", 13, kNoAttribute) \
|
|
\
|
|
/* Multiplicative operators */ \
|
|
TOK(kMUL, "*", 14, kNoAttribute) \
|
|
TOK(kDIV, "/", 14, kNoAttribute) \
|
|
TOK(kTRUNCDIV, "~/", 14, kNoAttribute) \
|
|
TOK(kMOD, "%", 14, kNoAttribute) \
|
|
\
|
|
TOK(kNOT, "!", 0, kNoAttribute) \
|
|
TOK(kCONDITIONAL, "?", 3, kNoAttribute) \
|
|
TOK(kIFnullptr, "??", 4, kNoAttribute) \
|
|
\
|
|
/* Equality operators. */ \
|
|
/* Please update IsEqualityOperator() if you make */ \
|
|
/* any changes to this block. */ \
|
|
TOK(kEQ, "==", 7, kNoAttribute) \
|
|
TOK(kNE, "!=", 7, kNoAttribute) \
|
|
TOK(kEQ_STRICT, "===", 7, kNoAttribute) \
|
|
TOK(kNE_STRICT, "!==", 7, kNoAttribute) \
|
|
\
|
|
/* Relational operators. */ \
|
|
/* Please update IsRelationalOperator() if you make */ \
|
|
/* any changes to this block. */ \
|
|
TOK(kLT, "<", 8, kNoAttribute) \
|
|
TOK(kGT, ">", 8, kNoAttribute) \
|
|
TOK(kLTE, "<=", 8, kNoAttribute) \
|
|
TOK(kGTE, ">=", 8, kNoAttribute) \
|
|
\
|
|
/* Internal token for !(expr is Type) negative type test operator */ \
|
|
TOK(kISNOT, "", 11, kNoAttribute) \
|
|
\
|
|
TOK(kINDEX, "[]", 0, kNoAttribute) \
|
|
TOK(kASSIGN_INDEX, "[]=", 0, kNoAttribute) \
|
|
TOK(kNEGATE, "unary-", 0, kNoAttribute) \
|
|
\
|
|
TOK(kIDENT, "", 0, kNoAttribute) \
|
|
TOK(kSTRING, "", 0, kNoAttribute) \
|
|
TOK(kINTEGER, "", 0, kNoAttribute) \
|
|
TOK(kDOUBLE, "", 0, kNoAttribute) \
|
|
\
|
|
TOK(kINTERPOL_VAR, "$", 0, kNoAttribute) \
|
|
TOK(kINTERPOL_START, "${", 0, kNoAttribute) \
|
|
TOK(kINTERPOL_END, "}", 0, kNoAttribute) \
|
|
\
|
|
TOK(kAT, "@", 0, kNoAttribute) \
|
|
TOK(kHASH, "#", 0, kNoAttribute) \
|
|
\
|
|
TOK(kNEWLINE, "\n", 0, kNoAttribute) \
|
|
TOK(kWHITESP, "", 0, kNoAttribute) \
|
|
TOK(kERROR, "", 0, kNoAttribute) \
|
|
TOK(kILLEGAL, "", 0, kNoAttribute) \
|
|
\
|
|
/* Support for Dart scripts. */ \
|
|
TOK(kSCRIPTTAG, "#!", 0, kNoAttribute) \
|
|
\
|
|
/* Support for optimized code */ \
|
|
TOK(kREM, "rem", 0, kNoAttribute) \
|
|
TOK(kABS, "abs", 0, kNoAttribute) \
|
|
TOK(kSQRT, "sqrt", 0, kNoAttribute) \
|
|
TOK(kMIN, "min", 0, kNoAttribute) \
|
|
TOK(kMAX, "max", 0, kNoAttribute) \
|
|
TOK(kRECIPROCAL, "reciprocal", 0, kNoAttribute) \
|
|
TOK(kRECIPROCAL_SQRT, "reciprocal-sqrt", 0, kNoAttribute) \
|
|
TOK(kSQUARE, "square", 0, kNoAttribute) \
|
|
TOK(kTRUNCATE, "truncate", 0, kNoAttribute) \
|
|
TOK(kFLOOR, "floor", 0, kNoAttribute) \
|
|
TOK(kCEILING, "ceiling", 0, kNoAttribute)
|
|
|
|
// List of keywords. The list must be alphabetically ordered. The
|
|
// keyword recognition code depends on the ordering.
|
|
// If you add a keyword at the beginning or end of this list, make sure
|
|
// to update kFirstKeyword and kLastKeyword below.
|
|
#define DART_KEYWORD_LIST(KW) \
|
|
KW(kABSTRACT, "abstract", 0, kPseudoKeyword) /* == kFirstKeyword */ \
|
|
KW(kAS, "as", 11, kPseudoKeyword) \
|
|
KW(kASSERT, "assert", 0, kKeyword) \
|
|
KW(kBREAK, "break", 0, kKeyword) \
|
|
KW(kCASE, "case", 0, kKeyword) \
|
|
KW(kCATCH, "catch", 0, kKeyword) \
|
|
KW(kCLASS, "class", 0, kKeyword) \
|
|
KW(kCONST, "const", 0, kKeyword) \
|
|
KW(kCONTINUE, "continue", 0, kKeyword) \
|
|
KW(kCOVARIANT, "covariant", 0, kPseudoKeyword) \
|
|
KW(kDEFAULT, "default", 0, kKeyword) \
|
|
KW(kDEFERRED, "deferred", 0, kPseudoKeyword) \
|
|
KW(kDO, "do", 0, kKeyword) \
|
|
KW(kELSE, "else", 0, kKeyword) \
|
|
KW(kENUM, "enum", 0, kKeyword) \
|
|
KW(kEXPORT, "export", 0, kPseudoKeyword) \
|
|
KW(kEXTENDS, "extends", 0, kKeyword) \
|
|
KW(kEXTERNAL, "external", 0, kPseudoKeyword) \
|
|
KW(kFACTORY, "factory", 0, kPseudoKeyword) \
|
|
KW(kFALSE, "false", 0, kKeyword) \
|
|
KW(kFINAL, "final", 0, kKeyword) \
|
|
KW(kFINALLY, "finally", 0, kKeyword) \
|
|
KW(kFOR, "for", 0, kKeyword) \
|
|
KW(kGET, "get", 0, kPseudoKeyword) \
|
|
KW(kIF, "if", 0, kKeyword) \
|
|
KW(kIMPLEMENTS, "implements", 0, kPseudoKeyword) \
|
|
KW(kIMPORT, "import", 0, kPseudoKeyword) \
|
|
KW(kIN, "in", 0, kKeyword) \
|
|
KW(kIS, "is", 11, kKeyword) \
|
|
KW(kLIBRARY, "library", 0, kPseudoKeyword) \
|
|
KW(kNEW, "new", 0, kKeyword) \
|
|
KW(knullptr, "null", 0, kKeyword) \
|
|
KW(kOPERATOR, "operator", 0, kPseudoKeyword) \
|
|
KW(kPART, "part", 0, kPseudoKeyword) \
|
|
KW(kRETHROW, "rethrow", 0, kKeyword) \
|
|
KW(kRETURN, "return", 0, kKeyword) \
|
|
KW(kSET, "set", 0, kPseudoKeyword) \
|
|
KW(kSTATIC, "static", 0, kPseudoKeyword) \
|
|
KW(kSUPER, "super", 0, kKeyword) \
|
|
KW(kSWITCH, "switch", 0, kKeyword) \
|
|
KW(kTHIS, "this", 0, kKeyword) \
|
|
KW(kTHROW, "throw", 0, kKeyword) \
|
|
KW(kTRUE, "true", 0, kKeyword) \
|
|
KW(kTRY, "try", 0, kKeyword) \
|
|
KW(kTYPEDEF, "typedef", 0, kPseudoKeyword) \
|
|
KW(kVAR, "var", 0, kKeyword) \
|
|
KW(kVOID, "void", 0, kKeyword) \
|
|
KW(kWHILE, "while", 0, kKeyword) \
|
|
KW(kWITH, "with", 0, kKeyword) /* == kLastKeyword */
|
|
|
|
class String;
|
|
|
|
class Token {
|
|
public:
|
|
#define T(t, s, p, a) t,
|
|
enum Kind { DART_TOKEN_LIST(T) DART_KEYWORD_LIST(T) kNumTokens };
|
|
#undef T
|
|
|
|
enum Attribute {
|
|
kNoAttribute = 0,
|
|
kKeyword = 1 << 0,
|
|
kPseudoKeyword = 1 << 1,
|
|
};
|
|
|
|
static const Kind kFirstKeyword = kABSTRACT;
|
|
static const Kind kLastKeyword = kWITH;
|
|
static constexpr int kNumKeywords = kLastKeyword - kFirstKeyword + 1;
|
|
|
|
static bool IsCommutativeOp(Kind op) {
|
|
switch (op) {
|
|
case Token::kMUL:
|
|
FALL_THROUGH;
|
|
case Token::kADD:
|
|
FALL_THROUGH;
|
|
case Token::kBIT_AND:
|
|
FALL_THROUGH;
|
|
case Token::kBIT_OR:
|
|
FALL_THROUGH;
|
|
case Token::kBIT_XOR:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool IsAssignmentOperator(Kind tok) {
|
|
return kASSIGN <= tok && tok <= kASSIGN_COND;
|
|
}
|
|
|
|
static bool IsRelationalOperator(Kind tok) {
|
|
return kLT <= tok && tok <= kGTE;
|
|
}
|
|
|
|
static bool IsEqualityOperator(Kind tok) {
|
|
return kEQ <= tok && tok <= kNE_STRICT;
|
|
}
|
|
|
|
static bool IsStrictEqualityOperator(Kind tok) {
|
|
return (tok == kEQ_STRICT) || (tok == kNE_STRICT);
|
|
}
|
|
|
|
static bool IsTypeTestOperator(Kind tok) {
|
|
return (tok == kIS) || (tok == kISNOT);
|
|
}
|
|
|
|
static bool IsTypeCastOperator(Kind tok) { return tok == kAS; }
|
|
|
|
static bool IsIndexOperator(Kind tok) {
|
|
return tok == kINDEX || tok == kASSIGN_INDEX;
|
|
}
|
|
|
|
static bool IsPseudoKeyword(Kind tok) {
|
|
return (Attributes(tok) & kPseudoKeyword) != 0;
|
|
}
|
|
|
|
static bool IsKeyword(Kind tok) { return (Attributes(tok) & kKeyword) != 0; }
|
|
|
|
static bool IsIdentifier(Kind tok) {
|
|
return (tok == kIDENT) || IsPseudoKeyword(tok);
|
|
}
|
|
|
|
static const char* Name(Kind tok) {
|
|
ASSERT(tok < kNumTokens);
|
|
return name_[tok];
|
|
}
|
|
|
|
static const char* Str(Kind tok) {
|
|
ASSERT(tok < kNumTokens);
|
|
return tok_str_[tok];
|
|
}
|
|
|
|
static bool FromStr(const char* str, Kind* out) {
|
|
ASSERT(str != nullptr && out != nullptr);
|
|
#define TOK_CASE(t, s, p, a) \
|
|
if (strcmp(str, tok_str_[(t)]) == 0) { \
|
|
*out = (t); \
|
|
return true; \
|
|
}
|
|
DART_TOKEN_LIST(TOK_CASE)
|
|
DART_KEYWORD_LIST(TOK_CASE)
|
|
#undef TOK_CASE
|
|
return false;
|
|
}
|
|
|
|
static int Precedence(Kind tok) {
|
|
ASSERT(tok < kNumTokens);
|
|
return precedence_[tok];
|
|
}
|
|
|
|
static Attribute Attributes(Kind tok) {
|
|
ASSERT(tok < kNumTokens);
|
|
return attributes_[tok];
|
|
}
|
|
|
|
static bool CanBeOverloaded(Kind tok) {
|
|
ASSERT(tok < kNumTokens);
|
|
return IsRelationalOperator(tok) || (tok == kEQ) ||
|
|
(tok >= kADD && tok <= kMOD) || // Arithmetic operations.
|
|
(tok >= kBIT_OR && tok <= kUSHR) || // Bit operations.
|
|
(tok == kINDEX) || (tok == kASSIGN_INDEX);
|
|
}
|
|
|
|
static bool NeedsLiteralToken(Kind tok) {
|
|
ASSERT(tok < kNumTokens);
|
|
return ((tok == Token::kINTEGER) || (tok == Token::kSTRING) ||
|
|
(tok == Token::kINTERPOL_VAR) || (tok == Token::kERROR) ||
|
|
(tok == Token::kDOUBLE));
|
|
}
|
|
|
|
static bool IsBinaryOperator(Token::Kind token);
|
|
static bool IsUnaryOperator(Token::Kind token);
|
|
|
|
static bool IsBinaryArithmeticOperator(Token::Kind token);
|
|
static bool IsUnaryArithmeticOperator(Token::Kind token);
|
|
|
|
static bool IsBinaryBitwiseOperator(Token::Kind token);
|
|
|
|
// For a comparison operation return an operation for the negated comparison:
|
|
// !(a (op) b) === a (op') b
|
|
static Token::Kind NegateComparison(Token::Kind op) {
|
|
switch (op) {
|
|
case Token::kEQ:
|
|
return Token::kNE;
|
|
case Token::kNE:
|
|
return Token::kEQ;
|
|
case Token::kLT:
|
|
return Token::kGTE;
|
|
case Token::kGT:
|
|
return Token::kLTE;
|
|
case Token::kLTE:
|
|
return Token::kGT;
|
|
case Token::kGTE:
|
|
return Token::kLT;
|
|
case Token::kEQ_STRICT:
|
|
return Token::kNE_STRICT;
|
|
case Token::kNE_STRICT:
|
|
return Token::kEQ_STRICT;
|
|
case Token::kIS:
|
|
return Token::kISNOT;
|
|
case Token::kISNOT:
|
|
return Token::kIS;
|
|
default:
|
|
UNREACHABLE();
|
|
return Token::kILLEGAL;
|
|
}
|
|
}
|
|
|
|
// For a comparison operation return an operation for the equivalent flipped
|
|
// comparison: a (op) b === b (op') a.
|
|
static Token::Kind FlipComparison(Token::Kind op) {
|
|
switch (op) {
|
|
case Token::kEQ:
|
|
return Token::kEQ;
|
|
case Token::kNE:
|
|
return Token::kNE;
|
|
case Token::kLT:
|
|
return Token::kGT;
|
|
case Token::kGT:
|
|
return Token::kLT;
|
|
case Token::kLTE:
|
|
return Token::kGTE;
|
|
case Token::kGTE:
|
|
return Token::kLTE;
|
|
case Token::kEQ_STRICT:
|
|
return Token::kEQ_STRICT;
|
|
case Token::kNE_STRICT:
|
|
return Token::kNE_STRICT;
|
|
default:
|
|
UNREACHABLE();
|
|
return Token::kILLEGAL;
|
|
}
|
|
}
|
|
|
|
private:
|
|
static const char* const name_[];
|
|
static const char* const tok_str_[];
|
|
static const uint8_t precedence_[];
|
|
static const Attribute attributes_[];
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_TOKEN_H_
|