[torque] better static assert error messages

When mksnapshot fails on a static assert in Torque, print the
statement and position from the Torque source. To enable special
treatment, change the syntax of static asserts in Torque
from StaticAssert() to static_assert() to align with assert() and
check() statements.

Bug: v8:7793
Change-Id: Idda8e3c342bdcefc893ff297f8d7727d2734c221
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2317314
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: Seth Brenith <seth.brenith@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#69069}
This commit is contained in:
Tobias Tebbi 2020-07-27 12:35:36 +02:00 committed by Commit Bot
parent c0ed24a0e5
commit a7ca5b0f6b
21 changed files with 107 additions and 61 deletions

View File

@ -473,7 +473,6 @@ extern macro SelectBooleanConstant(bool): Boolean;
extern macro Print(constexpr string);
extern macro Print(constexpr string, Object);
extern macro Comment(constexpr string);
extern macro StaticAssert(bool);
extern macro Print(Object);
extern macro DebugBreak();
@ -1618,7 +1617,7 @@ transitioning builtin FastCreateDataProperty(implicit context: Context)(
}
macro VerifiedUnreachable(): never {
StaticAssert(false);
static_assert(false);
unreachable;
}

View File

@ -117,7 +117,7 @@ IfInBounds(String, uintptr, uintptr), IfOutOfBounds {
// check.
const kMaxStringLengthFitsSmi: constexpr bool =
kStringMaxLengthUintptr < kSmiMaxValue;
StaticAssert(kMaxStringLengthFitsSmi);
static_assert(kMaxStringLengthFitsSmi);
if (index >= length) goto IfOutOfBounds;
goto IfInBounds(string, index, length);
}

View File

@ -49,7 +49,7 @@ FromConstexpr<Number, constexpr int31>(i: constexpr int31): Number {
}
FromConstexpr<uint8, constexpr int31>(i: constexpr int31): uint8 {
const i: uint32 = i;
StaticAssert(i <= 255);
static_assert(i <= 255);
return %RawDownCast<uint8>(i);
}
FromConstexpr<Number, constexpr Smi>(s: constexpr Smi): Number {

View File

@ -100,7 +100,7 @@ transitioning macro MorphAndEnqueuePromiseReaction(implicit context: Context)(
primaryHandler = promiseReaction.fulfill_handler;
secondaryHandler = promiseReaction.reject_handler;
} else {
StaticAssert(reactionType == kPromiseReactionReject);
static_assert(reactionType == kPromiseReactionReject);
primaryHandler = promiseReaction.reject_handler;
secondaryHandler = promiseReaction.fulfill_handler;
}
@ -114,7 +114,7 @@ transitioning macro MorphAndEnqueuePromiseReaction(implicit context: Context)(
// Morph {current} from a PromiseReaction into a PromiseReactionJobTask
// and schedule that on the microtask queue. We try to minimize the number
// of stores here to avoid screwing up the store buffer.
StaticAssert(
static_assert(
kPromiseReactionSize ==
kPromiseReactionJobTaskSizeOfAllPromiseReactionJobTasks);
if constexpr (reactionType == kPromiseReactionFulfill) {
@ -125,14 +125,14 @@ transitioning macro MorphAndEnqueuePromiseReaction(implicit context: Context)(
promiseReactionJobTask.argument = argument;
promiseReactionJobTask.context = handlerContext;
EnqueueMicrotask(handlerContext, promiseReactionJobTask);
StaticAssert(
static_assert(
kPromiseReactionFulfillHandlerOffset ==
kPromiseReactionJobTaskHandlerOffset);
StaticAssert(
static_assert(
kPromiseReactionPromiseOrCapabilityOffset ==
kPromiseReactionJobTaskPromiseOrCapabilityOffset);
} else {
StaticAssert(reactionType == kPromiseReactionReject);
static_assert(reactionType == kPromiseReactionReject);
*UnsafeConstCast(&promiseReaction.map) =
PromiseRejectReactionJobTaskMapConstant();
const promiseReactionJobTask =
@ -141,7 +141,7 @@ transitioning macro MorphAndEnqueuePromiseReaction(implicit context: Context)(
promiseReactionJobTask.context = handlerContext;
promiseReactionJobTask.handler = primaryHandler;
EnqueueMicrotask(handlerContext, promiseReactionJobTask);
StaticAssert(
static_assert(
kPromiseReactionPromiseOrCapabilityOffset ==
kPromiseReactionJobTaskPromiseOrCapabilityOffset);
}

View File

@ -30,7 +30,7 @@ macro RejectPromiseReactionJob(
}
}
} else {
StaticAssert(reactionType == kPromiseReactionFulfill);
static_assert(reactionType == kPromiseReactionFulfill);
// We have to call out to the dedicated PromiseRejectReactionJob
// builtin here, instead of just doing the work inline, as otherwise
// the catch predictions in the debugger will be wrong, which just
@ -79,7 +79,7 @@ macro PromiseReactionJob(
return FuflfillPromiseReactionJob(
context, promiseOrCapability, argument, reactionType);
} else {
StaticAssert(reactionType == kPromiseReactionReject);
static_assert(reactionType == kPromiseReactionReject);
return RejectPromiseReactionJob(
context, promiseOrCapability, argument, reactionType);
}

View File

@ -110,7 +110,7 @@ transitioning macro RegExpPrototypeMatchBody(implicit context: Context)(
// length is less than the maximal Smi value.
const kMaxStringLengthFitsSmi: constexpr bool =
kStringMaxLengthUintptr < kSmiMaxValue;
StaticAssert(kMaxStringLengthFitsSmi);
static_assert(kMaxStringLengthFitsSmi);
assert(TaggedIsPositiveSmi(newLastIndex));
}

View File

@ -219,6 +219,8 @@ macro DownCastForTorqueClass<T : type extends HeapObject>(o: HeapObject):
return %RawDownCast<T>(o);
}
extern macro StaticAssert(bool, constexpr string);
} // namespace torque_internal
// Indicates that an array-field should not be initialized.

View File

@ -3083,7 +3083,9 @@ void InstructionSelector::VisitUnreachable(Node* node) {
void InstructionSelector::VisitStaticAssert(Node* node) {
Node* asserted = node->InputAt(0);
asserted->Print(4);
FATAL("Expected turbofan static assert to hold, but got non-true input!\n");
FATAL(
"Expected Turbofan static assert to hold, but got non-true input:\n %s",
StaticAssertSourceOf(node->op()));
}
void InstructionSelector::VisitDeadValue(Node* node) {

View File

@ -495,8 +495,8 @@ void CodeAssembler::Comment(std::string str) {
raw_assembler()->Comment(str);
}
void CodeAssembler::StaticAssert(TNode<BoolT> value) {
raw_assembler()->StaticAssert(value);
void CodeAssembler::StaticAssert(TNode<BoolT> value, const char* source) {
raw_assembler()->StaticAssert(value, source);
}
void CodeAssembler::SetSourcePosition(const char* file, int line) {

View File

@ -597,7 +597,8 @@ class V8_EXPORT_PRIVATE CodeAssembler {
Comment(s.str());
}
void StaticAssert(TNode<BoolT> value);
void StaticAssert(TNode<BoolT> value,
const char* source = "unknown position");
// The following methods refer to source positions in CSA or Torque code
// compiled during mksnapshot, not JS compiled at runtime.

View File

@ -455,22 +455,21 @@ IfValueParameters const& IfValueParametersOf(const Operator* op) {
return OpParameter<IfValueParameters>(op);
}
#define COMMON_CACHED_OP_LIST(V) \
V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \
V(Unreachable, Operator::kFoldable, 0, 1, 1, 1, 1, 0) \
V(IfTrue, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(IfFalse, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(IfSuccess, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(IfException, Operator::kKontrol, 0, 1, 1, 1, 1, 1) \
V(Throw, Operator::kKontrol, 0, 1, 1, 0, 0, 1) \
V(Terminate, Operator::kKontrol, 0, 1, 1, 0, 0, 1) \
V(LoopExit, Operator::kKontrol, 0, 0, 2, 0, 0, 1) \
V(LoopExitValue, Operator::kPure, 1, 0, 1, 1, 0, 0) \
V(LoopExitEffect, Operator::kNoThrow, 0, 1, 1, 0, 1, 0) \
V(Checkpoint, Operator::kKontrol, 0, 1, 1, 0, 1, 0) \
V(FinishRegion, Operator::kKontrol, 1, 1, 0, 1, 1, 0) \
V(Retain, Operator::kKontrol, 1, 1, 0, 0, 1, 0) \
V(StaticAssert, Operator::kFoldable, 1, 1, 0, 0, 1, 0)
#define COMMON_CACHED_OP_LIST(V) \
V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \
V(Unreachable, Operator::kFoldable, 0, 1, 1, 1, 1, 0) \
V(IfTrue, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(IfFalse, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(IfSuccess, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(IfException, Operator::kKontrol, 0, 1, 1, 1, 1, 1) \
V(Throw, Operator::kKontrol, 0, 1, 1, 0, 0, 1) \
V(Terminate, Operator::kKontrol, 0, 1, 1, 0, 0, 1) \
V(LoopExit, Operator::kKontrol, 0, 0, 2, 0, 0, 1) \
V(LoopExitValue, Operator::kPure, 1, 0, 1, 1, 0, 0) \
V(LoopExitEffect, Operator::kNoThrow, 0, 1, 1, 0, 1, 0) \
V(Checkpoint, Operator::kKontrol, 0, 1, 1, 0, 1, 0) \
V(FinishRegion, Operator::kKontrol, 1, 1, 0, 1, 1, 0) \
V(Retain, Operator::kKontrol, 1, 1, 0, 0, 1, 0)
#define CACHED_BRANCH_LIST(V) \
V(None, CriticalSafetyCheck) \
@ -939,6 +938,12 @@ const Operator* CommonOperatorBuilder::Return(int value_input_count) {
value_input_count + 1, 1, 1, 0, 0, 1); // counts
}
const Operator* CommonOperatorBuilder::StaticAssert(const char* source) {
return zone()->New<Operator1<const char*>>(
IrOpcode::kStaticAssert, Operator::kFoldable, "StaticAssert", 1, 1, 0, 0,
1, 0, source);
}
const Operator* CommonOperatorBuilder::Branch(BranchHint hint,
IsSafetyCheck is_safety_check) {
#define CACHED_BRANCH(Hint, IsCheck) \
@ -1248,6 +1253,11 @@ const StringConstantBase* StringConstantBaseOf(const Operator* op) {
return OpParameter<const StringConstantBase*>(op);
}
const char* StaticAssertSourceOf(const Operator* op) {
DCHECK_EQ(IrOpcode::kStaticAssert, op->opcode());
return OpParameter<const char*>(op);
}
const Operator* CommonOperatorBuilder::RelocatableInt32Constant(
int32_t value, RelocInfo::Mode rmode) {
return zone()->New<Operator1<RelocatablePtrConstantInfo>>( // --

View File

@ -449,6 +449,8 @@ V8_EXPORT_PRIVATE Handle<HeapObject> HeapConstantOf(const Operator* op)
const StringConstantBase* StringConstantBaseOf(const Operator* op)
V8_WARN_UNUSED_RESULT;
const char* StaticAssertSourceOf(const Operator* op);
// Interface for building common operators that can be used at any level of IR,
// including JavaScript, mid-level, and low-level.
class V8_EXPORT_PRIVATE CommonOperatorBuilder final
@ -459,7 +461,7 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final
const Operator* Dead();
const Operator* DeadValue(MachineRepresentation rep);
const Operator* Unreachable();
const Operator* StaticAssert();
const Operator* StaticAssert(const char* source);
const Operator* End(size_t control_input_count);
const Operator* Branch(BranchHint = BranchHint::kNone,
IsSafetyCheck = IsSafetyCheck::kSafetyCheck);

View File

@ -285,7 +285,8 @@ Reduction JSIntrinsicLowering::ReduceTurbofanStaticAssert(Node* node) {
} else {
Node* value = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
Node* assert = graph()->NewNode(common()->StaticAssert(), value, effect);
Node* assert = graph()->NewNode(
common()->StaticAssert("%TurbofanStaticAssert"), value, effect);
ReplaceWithValue(node, node, assert, nullptr);
}
return Changed(jsgraph_->UndefinedConstant());

View File

@ -680,8 +680,8 @@ void RawMachineAssembler::Comment(const std::string& msg) {
AddNode(machine()->Comment(zone_buffer));
}
void RawMachineAssembler::StaticAssert(Node* value) {
AddNode(common()->StaticAssert(), value);
void RawMachineAssembler::StaticAssert(Node* value, const char* source) {
AddNode(common()->StaticAssert(source), value);
}
Node* RawMachineAssembler::CallN(CallDescriptor* call_descriptor,

View File

@ -967,7 +967,7 @@ class V8_EXPORT_PRIVATE RawMachineAssembler {
void DebugBreak();
void Unreachable();
void Comment(const std::string& msg);
void StaticAssert(Node* value);
void StaticAssert(Node* value, const char* source);
#if DEBUG
void Bind(RawMachineLabel* label, AssemblerDebugInfo info);

View File

@ -709,13 +709,14 @@ struct DebugStatement : Statement {
struct AssertStatement : Statement {
DEFINE_AST_NODE_LEAF_BOILERPLATE(AssertStatement)
AssertStatement(SourcePosition pos, bool debug_only, Expression* expression,
enum class AssertKind { kAssert, kCheck, kStaticAssert };
AssertStatement(SourcePosition pos, AssertKind kind, Expression* expression,
std::string source)
: Statement(kKind, pos),
debug_only(debug_only),
kind(kind),
expression(expression),
source(std::move(source)) {}
bool debug_only;
AssertKind kind;
Expression* expression;
std::string source;
};

View File

@ -17,6 +17,7 @@ namespace torque {
static const char* const CONSTEXPR_TYPE_PREFIX = "constexpr ";
static const char* const NEVER_TYPE_STRING = "never";
static const char* const CONSTEXPR_BOOL_TYPE_STRING = "constexpr bool";
static const char* const CONSTEXPR_STRING_TYPE_STRING = "constexpr string";
static const char* const CONSTEXPR_INTPTR_TYPE_STRING = "constexpr intptr";
static const char* const CONSTEXPR_INSTANCE_TYPE_TYPE_STRING =
"constexpr InstanceType";
@ -75,6 +76,7 @@ static const char* const UNINITIALIZED_ITERATOR_TYPE_STRING =
static const char* const GENERIC_TYPE_INSTANTIATION_NAMESPACE_STRING =
"_generic_type_instantiation_namespace";
static const char* const FIXED_ARRAY_BASE_TYPE_STRING = "FixedArrayBase";
static const char* const STATIC_ASSERT_MACRO_STRING = "StaticAssert";
static const char* const ANNOTATION_GENERATE_PRINT = "@generatePrint";
static const char* const ANNOTATION_NO_VERIFIER = "@noVerifier";

View File

@ -10,6 +10,7 @@
#include "src/base/optional.h"
#include "src/common/globals.h"
#include "src/torque/constants.h"
#include "src/torque/csa-generator.h"
#include "src/torque/declaration-visitor.h"
#include "src/torque/global-context.h"
@ -1047,7 +1048,19 @@ std::string FormatAssertSource(const std::string& str) {
} // namespace
const Type* ImplementationVisitor::Visit(AssertStatement* stmt) {
bool do_check = !stmt->debug_only || GlobalContext::force_assert_statements();
if (stmt->kind == AssertStatement::AssertKind::kStaticAssert) {
std::string message =
"static_assert(" + stmt->source + ") at " + ToString(stmt->pos);
GenerateCall(QualifiedName({"", TORQUE_INTERNAL_NAMESPACE_STRING},
STATIC_ASSERT_MACRO_STRING),
Arguments{{Visit(stmt->expression),
VisitResult(TypeOracle::GetConstexprStringType(),
StringLiteralQuote(message))},
{}});
return TypeOracle::GetVoidType();
}
bool do_check = stmt->kind != AssertStatement::AssertKind::kAssert ||
GlobalContext::force_assert_statements();
#if defined(DEBUG)
do_check = true;
#endif

View File

@ -487,11 +487,20 @@ base::Optional<ParseResult> MakeParameterList(
base::Optional<ParseResult> MakeAssertStatement(
ParseResultIterator* child_results) {
auto kind = child_results->NextAs<Identifier*>()->value;
auto kind_string = child_results->NextAs<Identifier*>()->value;
auto expr_with_source = child_results->NextAs<ExpressionWithSource>();
DCHECK(kind == "assert" || kind == "check");
AssertStatement::AssertKind kind;
if (kind_string == "assert") {
kind = AssertStatement::AssertKind::kAssert;
} else if (kind_string == "check") {
kind = AssertStatement::AssertKind::kCheck;
} else if (kind_string == "static_assert") {
kind = AssertStatement::AssertKind::kStaticAssert;
} else {
UNREACHABLE();
}
Statement* result = MakeNode<AssertStatement>(
kind == "assert", expr_with_source.expression, expr_with_source.source);
kind, expr_with_source.expression, expr_with_source.source);
return ParseResult{result};
}
@ -2400,8 +2409,8 @@ struct TorqueGrammar : Grammar {
MakeTypeswitchStatement),
Rule({Token("try"), &block, List<TryHandler*>(&tryHandler)},
MakeTryLabelExpression),
Rule({OneOf({"assert", "check"}), Token("("), &expressionWithSource,
Token(")"), Token(";")},
Rule({OneOf({"assert", "check", "static_assert"}), Token("("),
&expressionWithSource, Token(")"), Token(";")},
MakeAssertStatement),
Rule({Token("while"), Token("("), expression, Token(")"), &statement},
MakeWhileStatement),

View File

@ -166,6 +166,10 @@ class TypeOracle : public ContextualClass<TypeOracle> {
return Get().GetBuiltinType(CONSTEXPR_BOOL_TYPE_STRING);
}
static const Type* GetConstexprStringType() {
return Get().GetBuiltinType(CONSTEXPR_STRING_TYPE_STRING);
}
static const Type* GetConstexprIntPtrType() {
return Get().GetBuiltinType(CONSTEXPR_INTPTR_TYPE_STRING);
}

View File

@ -908,7 +908,7 @@ macro TestSliceEnumeration(implicit context: Context)(): Undefined {
@export
macro TestStaticAssert() {
StaticAssert(1 + 2 == 3);
static_assert(1 + 2 == 3);
}
class SmiBox extends HeapObject {
@ -926,12 +926,12 @@ macro TestLoadEliminationFixed(implicit context: Context)() {
const v1 = box.value;
box.unrelated = 999;
const v2 = (box.unrelated == 0) ? box.value : box.value;
StaticAssert(TaggedEqual(v1, v2));
static_assert(TaggedEqual(v1, v2));
box.value = 11;
const v3 = box.value;
const eleven: Smi = 11;
StaticAssert(TaggedEqual(v3, eleven));
static_assert(TaggedEqual(v3, eleven));
}
@export
@ -942,8 +942,8 @@ macro TestLoadEliminationVariable(implicit context: Context)() {
const u1 = a.objects[box.value + 2];
const v2 = a.objects[box.value];
const u2 = a.objects[box.value + 2];
StaticAssert(TaggedEqual(v1, v2));
StaticAssert(TaggedEqual(u1, u2));
static_assert(TaggedEqual(v1, v2));
static_assert(TaggedEqual(u1, u2));
}
@export
@ -954,7 +954,7 @@ macro TestRedundantArrayElementCheck(implicit context: Context)(): Smi {
if (a.objects[i] == TheHole) {
return -1;
} else {
StaticAssert(false);
static_assert(false);
}
}
}
@ -1029,9 +1029,9 @@ macro TestBranchOnBoolOptimization(implicit context: Context)(input: Smi) {
// If the two branches get combined into one, we should be able to determine
// the value of {box} statically.
if (BranchAndWriteResult(input, box)) {
StaticAssert(box.value == 1);
static_assert(box.value == 1);
} else {
StaticAssert(box.value == 2);
static_assert(box.value == 2);
}
}
@ -1135,23 +1135,23 @@ macro TestBitFieldMultipleFlags(a: bool, b: int32, c: bool) {
const f = TestBitFieldStruct4{a: a, b: b, c: c};
let simpleExpression = f.a & f.b == 3 & !f.c;
let expectedReduction = (Signed(f) & 0x1f) == Convert<int32>(1 | 3 << 1);
StaticAssert(simpleExpression == expectedReduction);
static_assert(simpleExpression == expectedReduction);
simpleExpression = !f.a & f.b == 4 & f.c;
expectedReduction = (Signed(f) & 0x1f) == Convert<int32>(4 << 1 | 1 << 4);
StaticAssert(simpleExpression == expectedReduction);
static_assert(simpleExpression == expectedReduction);
simpleExpression = f.b == 0 & f.c;
expectedReduction = (Signed(f) & 0x1e) == Convert<int32>(1 << 4);
StaticAssert(simpleExpression == expectedReduction);
static_assert(simpleExpression == expectedReduction);
simpleExpression = f.a & f.c;
expectedReduction = (Signed(f) & 0x11) == Convert<int32>(1 | 1 << 4);
StaticAssert(simpleExpression == expectedReduction);
static_assert(simpleExpression == expectedReduction);
const f2 = TestBitFieldStruct5{b: b, a: a, c: c};
simpleExpression = !f2.a & f2.b == 1234 & f2.c;
expectedReduction = (Signed(f2) & 0x1fffff) == Convert<int32>(1234 | 1 << 20);
StaticAssert(simpleExpression == expectedReduction);
static_assert(simpleExpression == expectedReduction);
simpleExpression = !f2.a & !f2.c;
expectedReduction = (Signed(f2) & 0x180000) == Convert<int32>(0);
StaticAssert(simpleExpression == expectedReduction);
static_assert(simpleExpression == expectedReduction);
}
@export