Commit ec2ea85f authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

[torque] remove support for implicit branching on macros with labels

Now that we can short-circuit control flow in the optimizing compiler,
there is no more need for BranchIf... macros in CSA/Torque.
Thus removing support for them in Torque and rewriting Torque macros to
use bool return values instead.

Bug: v8:7793
Change-Id: Ie4b7522aa5558be038fe821d8b5d02859d522ed1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1724211
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63079}
parent 900f3a5d
......@@ -86,29 +86,25 @@ namespace array_join {
// (see LoadJoinElement<ElementsAccessor>).
macro CannotUseSameArrayAccessor<T: type>(implicit context: Context)(
loadFn: LoadJoinElementFn, receiver: JSReceiver, originalMap: Map,
originalLen: Number): never
labels Cannot, Can;
originalLen: Number): bool;
CannotUseSameArrayAccessor<JSArray>(implicit context: Context)(
loadFn: LoadJoinElementFn, receiver: JSReceiver, originalMap: Map,
originalLen: Number): never
labels Cannot, Can {
if (loadFn == LoadJoinElement<array::GenericElementsAccessor>) goto Can;
originalLen: Number): bool {
if (loadFn == LoadJoinElement<array::GenericElementsAccessor>) return false;
const array: JSArray = UnsafeCast<JSArray>(receiver);
if (originalMap != array.map) goto Cannot;
if (originalLen != array.length) goto Cannot;
if (IsNoElementsProtectorCellInvalid()) goto Cannot;
goto Can;
if (originalMap != array.map) return true;
if (originalLen != array.length) return true;
if (IsNoElementsProtectorCellInvalid()) return true;
return false;
}
CannotUseSameArrayAccessor<JSTypedArray>(implicit context: Context)(
_loadFn: LoadJoinElementFn, receiver: JSReceiver, _initialMap: Map,
_initialLen: Number): never
labels Cannot, Can {
_initialLen: Number): bool {
const typedArray: JSTypedArray = UnsafeCast<JSTypedArray>(receiver);
if (IsDetachedBuffer(typedArray.buffer)) goto Cannot;
goto Can;
return IsDetachedBuffer(typedArray.buffer);
}
// Calculates the running total length of the resulting string. If the
......@@ -465,11 +461,9 @@ namespace array_join {
}
// Fast path the common non-nested calls. If the receiver is not already on
// the stack, add it to the stack and go to ReceiverAdded. Otherwise go to
// ReceiverNotAdded.
// the stack, add it to the stack and return true. Otherwise return false.
macro JoinStackPushInline(implicit context: Context)(receiver: JSReceiver):
never
labels ReceiverAdded, ReceiverNotAdded {
bool {
try {
const stack: FixedArray = LoadJoinStack()
otherwise IfUninitialized;
......@@ -477,7 +471,7 @@ namespace array_join {
stack.objects[0] = receiver;
} else if (JoinStackPush(stack, receiver) == False)
deferred {
goto ReceiverNotAdded;
return false;
}
}
label IfUninitialized {
......@@ -486,7 +480,7 @@ namespace array_join {
stack.objects[0] = receiver;
SetJoinStack(stack);
}
goto ReceiverAdded;
return true;
}
// Removes a receiver from the stack. The FixedArray will automatically shrink
......
......@@ -1632,26 +1632,33 @@ extern operator '<' macro Float64LessThan(float64, float64): bool;
extern macro BranchIfNumberEqual(Number, Number): never
labels Taken, NotTaken;
operator '==' macro IsNumberEqual(a: Number, b: Number): bool {
return (BranchIfNumberEqual(a, b)) ? true : false;
BranchIfNumberEqual(a, b) otherwise return true, return false;
}
operator '!=' macro IsNumberNotEqual(a: Number, b: Number): bool {
return (BranchIfNumberEqual(a, b)) ? false : true;
return !(a == b);
}
extern operator '<' macro BranchIfNumberLessThan(Number, Number): never
extern macro BranchIfNumberLessThan(Number, Number): never
labels Taken, NotTaken;
extern operator '<=' macro BranchIfNumberLessThanOrEqual(Number, Number): never
operator '<' macro NumberIsLessThan(a: Number, b: Number): bool {
BranchIfNumberLessThan(a, b) otherwise return true, return false;
}
extern macro BranchIfNumberLessThanOrEqual(Number, Number): never
labels Taken, NotTaken;
operator '<=' macro NumberIsLessThanOrEqual(a: Number, b: Number): bool {
BranchIfNumberLessThanOrEqual(a, b) otherwise return true, return false;
}
extern operator '>' macro BranchIfNumberGreaterThan(Number, Number): never
labels Taken, NotTaken;
extern operator '>=' macro BranchIfNumberGreaterThanOrEqual(
Number, Number): never
labels Taken, NotTaken;
operator '>' macro NumberIsGreaterThan(a: Number, b: Number): bool {
return b < a;
}
operator '>=' macro NumberIsGreaterThanOrEqual(a: Number, b: Number): bool {
return b <= a;
}
extern macro BranchIfFloat64IsNaN(float64): never
labels Taken, NotTaken;
macro Float64IsNaN(n: float64): bool {
return (BranchIfFloat64IsNaN(n)) ? true : false;
BranchIfFloat64IsNaN(n) otherwise return true, return false;
}
// The type of all tagged values that can safely be compared with WordEqual.
......@@ -2020,7 +2027,7 @@ Cast<FastJSRegExp>(implicit context: Context)(o: HeapObject): FastJSRegExp
// the conditions to make a regexp object fast differ based on the callsite.
// For now, run the strict variant since replace (the only current callsite)
// accesses flag getters.
if (regexp::BranchIfFastRegExp_Strict(o)) {
if (regexp::IsFastRegExpStrict(o)) {
return %RawDownCast<FastJSRegExp>(o);
}
goto CastError;
......@@ -2392,20 +2399,9 @@ Convert<bint, Smi>(v: Smi): bint {
return SmiToBInt(v);
}
macro BranchIf<A: type, B: type>(implicit context: Context)(o: B): never
labels True, False {
Cast<A>(o) otherwise False;
goto True;
}
macro BranchIfNot<A: type, B: type>(implicit context: Context)(o: B): never
labels True, False {
Cast<A>(o) otherwise True;
goto False;
}
macro Is<A: type, B: type>(implicit context: Context)(o: B): bool {
return (BranchIf<A, B>(o)) ? true : false;
Cast<A>(o) otherwise return false;
return true;
}
macro UnsafeCast<A: type>(implicit context: Context)(o: Object): A {
......@@ -2918,17 +2914,18 @@ macro NumberIsNaN(number: Number): bool {
}
extern macro GotoIfForceSlowPath() labels Taken;
macro IsForceSlowPath(): bool {
GotoIfForceSlowPath() otherwise return true;
return false;
}
extern macro BranchIfToBooleanIsTrue(Object): never
labels Taken, NotTaken;
extern macro BranchIfToBooleanIsFalse(Object): never
labels Taken, NotTaken;
macro ToBoolean(obj: Object): bool {
if (BranchIfToBooleanIsTrue(obj)) {
return true;
} else {
return false;
}
BranchIfToBooleanIsTrue(obj) otherwise return true, return false;
}
@export
......@@ -2941,6 +2938,9 @@ macro RequireObjectCoercible(implicit context: Context)(
}
extern macro BranchIfSameValue(Object, Object): never labels Taken, NotTaken;
macro SameValue(a: Object, b: Object): bool {
BranchIfSameValue(a, b) otherwise return true, return false;
}
transitioning macro ToIndex(input: Object, context: Context): Number
labels RangeError {
......@@ -3005,25 +3005,20 @@ struct KeyValuePair {
// using "legacy" APIs. In Torque code, these should not be used.
@export
macro IsFastJSArray(o: Object, context: Context): bool {
try {
// Long-term, it's likely not a good idea to have this slow-path test here,
// since it fundamentally breaks the type system.
GotoIfForceSlowPath() otherwise ForceSlow;
}
label ForceSlow {
return false;
}
// Long-term, it's likely not a good idea to have this slow-path test here,
// since it fundamentally breaks the type system.
if (IsForceSlowPath()) return false;
return Is<FastJSArray>(o);
}
@export
macro BranchIfFastJSArray(o: Object, context: Context): never labels True,
False {
// Long-term, it's likely not a good idea to have this slow-path test here,
// since it fundamentally breaks the type system.
GotoIfForceSlowPath() otherwise False;
BranchIf<FastJSArray>(o) otherwise True, False;
if (IsFastJSArray(o, context)) {
goto True;
} else {
goto False;
}
}
@export
......@@ -3031,8 +3026,12 @@ macro BranchIfFastJSArrayForRead(o: Object, context: Context):
never labels True, False {
// Long-term, it's likely not a good idea to have this slow-path test here,
// since it fundamentally breaks the type system.
GotoIfForceSlowPath() otherwise False;
BranchIf<FastJSArrayForRead>(o) otherwise True, False;
if (IsForceSlowPath()) goto False;
if (Is<FastJSArrayForRead>(o)) {
goto True;
} else {
goto False;
}
}
@export
......
......@@ -40,7 +40,7 @@ namespace proxy {
const trapResult = Call(context, trap, handler, target, name);
// 9. If booleanTrapResult is false, return false.
if (BranchIfToBooleanIsFalse(trapResult)) {
if (!ToBoolean(trapResult)) {
if (languageMode == SmiConstant(kStrict)) {
ThrowTypeError(kProxyTrapReturnedFalsishFor, kTrapName, name);
}
......
......@@ -51,7 +51,7 @@ namespace proxy {
// 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError
// exception.
// 13. Return handlerProto.
if (BranchIfSameValue(targetProto, handlerProto)) {
if (SameValue(targetProto, handlerProto)) {
return handlerProto;
}
ThrowTypeError(kProxyGetPrototypeOfNonExtensible);
......
......@@ -40,7 +40,7 @@ namespace proxy {
// CheckHasTrapResult).
// 10. Return booleanTrapResult.
const trapResult = Call(context, trap, handler, target, name);
if (BranchIfToBooleanIsTrue(trapResult)) {
if (ToBoolean(trapResult)) {
return True;
}
CheckHasTrapResult(target, proxy, name);
......
......@@ -36,7 +36,7 @@ namespace proxy {
// 8. If booleanTrapResult is true, then
// 8.a. Let extensibleTarget be ? IsExtensible(target).
// 8.b If extensibleTarget is true, throw a TypeError exception.
if (BranchIfToBooleanIsTrue(trapResult)) {
if (ToBoolean(trapResult)) {
const extensibleTarget: Object = object::ObjectIsExtensible(target);
assert(extensibleTarget == True || extensibleTarget == False);
if (extensibleTarget == True) {
......
......@@ -62,7 +62,7 @@ namespace proxy {
// 12. Return true.
const trapResult =
Call(context, trap, handler, target, name, value, receiverValue);
if (BranchIfToBooleanIsTrue(trapResult)) {
if (ToBoolean(trapResult)) {
CheckGetSetTrapResult(target, proxy, name, value, kProxySet);
return value;
}
......
......@@ -37,7 +37,7 @@ namespace proxy {
const trapResult = Call(context, trap, handler, target, proto);
// 9. If booleanTrapResult is false, return false.
if (BranchIfToBooleanIsFalse(trapResult)) {
if (!ToBoolean(trapResult)) {
if (doThrow == True) {
ThrowTypeError(kProxyTrapReturnedFalsishFor, kTrapName);
}
......@@ -58,7 +58,7 @@ namespace proxy {
// 13. If SameValue(V, targetProto) is false, throw a TypeError
// exception.
// 14. Return true.
if (BranchIfSameValue(proto, targetProto)) {
if (SameValue(proto, targetProto)) {
return True;
}
ThrowTypeError(kProxySetPrototypeOfNonExtensible);
......
......@@ -9,5 +9,8 @@ namespace regexp {
extern macro RegExpBuiltinsAssembler::BranchIfFastRegExp_Strict(
implicit context: Context)(HeapObject): never labels IsFast,
IsSlow;
macro IsFastRegExpStrict(implicit context: Context)(o: HeapObject): bool {
BranchIfFastRegExp_Strict(o) otherwise return true, return false;
}
}
......@@ -53,7 +53,7 @@ namespace typed_array_filter {
// d. If selected is true, then
// i. Append kValue to the end of kept.
// ii. Increase captured by 1.
if (BranchIfToBooleanIsTrue(selected)) kept.Push(value);
if (ToBoolean(selected)) kept.Push(value);
// e.Increase k by 1.
}
......
......@@ -13,7 +13,7 @@ namespace typed_array_slice {
macro FastCopy(
src: typed_array::AttachedJSTypedArray, dest: JSTypedArray, k: intptr,
count: PositiveSmi) labels IfSlow {
GotoIfForceSlowPath() otherwise IfSlow;
if (IsForceSlowPath()) goto IfSlow;
const srcKind: ElementsKind = src.elements_kind;
const destInfo = typed_array::GetTypedArrayElementsInfo(dest);
......
......@@ -15,8 +15,6 @@ namespace internal {
namespace torque {
static constexpr const char* const kFromConstexprMacroName = "FromConstexpr";
static constexpr const char* kTrueLabelName = "__True";
static constexpr const char* kFalseLabelName = "__False";
static constexpr const char* kMacroEndLabelName = "__macro_end";
static constexpr const char* kBreakLabelName = "__break";
static constexpr const char* kContinueLabelName = "__continue";
......
......@@ -647,24 +647,8 @@ VisitResult ImplementationVisitor::Visit(ConditionalExpression* expr) {
}
VisitResult ImplementationVisitor::Visit(LogicalOrExpression* expr) {
VisitResult left_result;
{
Block* false_block = assembler().NewBlock(assembler().CurrentStack());
Binding<LocalLabel> false_binding{&LabelBindingsManager::Get(),
kFalseLabelName, LocalLabel{false_block}};
left_result = Visit(expr->left);
if (left_result.type()->IsBool()) {
Block* true_block = LookupSimpleLabel(kTrueLabelName);
assembler().Branch(true_block, false_block);
assembler().Bind(false_block);
} else if (left_result.type()->IsNever()) {
assembler().Bind(false_block);
} else if (!left_result.type()->IsConstexprBool()) {
ReportError(
"expected type bool, constexpr bool, or never on left-hand side of "
"operator ||");
}
}
StackScope outer_scope(this);
VisitResult left_result = Visit(expr->left);
if (left_result.type()->IsConstexprBool()) {
VisitResult right_result = Visit(expr->right);
......@@ -678,38 +662,34 @@ VisitResult ImplementationVisitor::Visit(LogicalOrExpression* expr) {
" || " + right_result.constexpr_value() + ")");
}
VisitResult right_result = Visit(expr->right);
if (right_result.type()->IsBool()) {
Block* true_block = LookupSimpleLabel(kTrueLabelName);
Block* false_block = LookupSimpleLabel(kFalseLabelName);
assembler().Branch(true_block, false_block);
return VisitResult::NeverResult();
} else if (!right_result.type()->IsNever()) {
ReportError(
"expected type bool or never on right-hand side of operator ||");
Block* true_block = assembler().NewBlock();
Block* false_block = assembler().NewBlock();
Block* done_block = assembler().NewBlock();
left_result = GenerateImplicitConvert(TypeOracle::GetBoolType(), left_result);
GenerateBranch(left_result, true_block, false_block);
assembler().Bind(true_block);
VisitResult true_result = GenerateBoolConstant(true);
assembler().Goto(done_block);
assembler().Bind(false_block);
VisitResult false_result;
{
StackScope false_block_scope(this);
false_result = false_block_scope.Yield(
GenerateImplicitConvert(TypeOracle::GetBoolType(), Visit(expr->right)));
}
return right_result;
assembler().Goto(done_block);
assembler().Bind(done_block);
DCHECK_EQ(true_result, false_result);
return outer_scope.Yield(true_result);
}
VisitResult ImplementationVisitor::Visit(LogicalAndExpression* expr) {
VisitResult left_result;
{
Block* true_block = assembler().NewBlock(assembler().CurrentStack());
Binding<LocalLabel> false_binding{&LabelBindingsManager::Get(),
kTrueLabelName, LocalLabel{true_block}};
left_result = Visit(expr->left);
if (left_result.type()->IsBool()) {
Block* false_block = LookupSimpleLabel(kFalseLabelName);
assembler().Branch(true_block, false_block);
assembler().Bind(true_block);
} else if (left_result.type()->IsNever()) {
assembler().Bind(true_block);
} else if (!left_result.type()->IsConstexprBool()) {
ReportError(
"expected type bool, constexpr bool, or never on left-hand side of "
"operator &&");
}
}
StackScope outer_scope(this);
VisitResult left_result = Visit(expr->left);
if (left_result.type()->IsConstexprBool()) {
VisitResult right_result = Visit(expr->right);
......@@ -723,17 +703,29 @@ VisitResult ImplementationVisitor::Visit(LogicalAndExpression* expr) {
" && " + right_result.constexpr_value() + ")");
}
VisitResult right_result = Visit(expr->right);
if (right_result.type()->IsBool()) {
Block* true_block = LookupSimpleLabel(kTrueLabelName);
Block* false_block = LookupSimpleLabel(kFalseLabelName);
assembler().Branch(true_block, false_block);
return VisitResult::NeverResult();
} else if (!right_result.type()->IsNever()) {
ReportError(
"expected type bool or never on right-hand side of operator &&");
Block* true_block = assembler().NewBlock();
Block* false_block = assembler().NewBlock();
Block* done_block = assembler().NewBlock();
left_result = GenerateImplicitConvert(TypeOracle::GetBoolType(), left_result);
GenerateBranch(left_result, true_block, false_block);
assembler().Bind(true_block);
VisitResult true_result;
{
StackScope true_block_scope(this);
true_result = true_block_scope.Yield(
GenerateImplicitConvert(TypeOracle::GetBoolType(), Visit(expr->right)));
}
return right_result;
assembler().Goto(done_block);
assembler().Bind(false_block);
VisitResult false_result = GenerateBoolConstant(false);
assembler().Goto(done_block);
assembler().Bind(done_block);
DCHECK_EQ(true_result, false_result);
return outer_scope.Yield(true_result);
}
VisitResult ImplementationVisitor::Visit(IncrementDecrementExpression* expr) {
......@@ -1661,11 +1653,7 @@ Callable* ImplementationVisitor::LookupCallable(
std::vector<size_t> candidates;
for (size_t i = 0; i < overloads.size(); ++i) {
const Signature& signature = overload_signatures[i];
bool try_bool_context = labels.size() == 0 &&
signature.return_type == TypeOracle::GetNeverType();
if (IsCompatibleSignature(signature, parameter_types, labels.size()) ||
(try_bool_context &&
IsCompatibleSignature(signature, parameter_types, 2))) {
if (IsCompatibleSignature(signature, parameter_types, labels.size())) {
candidates.push_back(i);
}
}
......@@ -2099,25 +2087,6 @@ VisitResult ImplementationVisitor::GenerateCall(
Callable* callable, base::Optional<LocationReference> this_reference,
Arguments arguments, const TypeVector& specialization_types,
bool is_tailcall) {
// Operators used in a branching context can also be function calls that never
// return but have a True and False label
if (arguments.labels.size() == 0 &&
callable->signature().labels.size() == 2) {
base::Optional<Binding<LocalLabel>*> true_label =
TryLookupLabel(kTrueLabelName);
base::Optional<Binding<LocalLabel>*> false_label =
TryLookupLabel(kFalseLabelName);
if (!true_label || !false_label) {
ReportError(
callable->ReadableName(),
" does not return a value, but has to be called in a branching "
"context (e.g., conditional or if-condition). You can fix this by "
"adding \"? true : false\".");
}
arguments.labels.push_back(*true_label);
arguments.labels.push_back(*false_label);
}
const Type* return_type = callable->signature().return_type;
if (is_tailcall) {
......@@ -2454,32 +2423,20 @@ void ImplementationVisitor::GenerateBranch(const VisitResult& condition,
assembler().Branch(true_block, false_block);
}
void ImplementationVisitor::GenerateExpressionBranch(
VisitResultGenerator generator, Block* true_block, Block* false_block) {
// Conditional expressions can either explicitly return a bit
// type, or they can be backed by macros that don't return but
// take a true and false label. By declaring the labels before
// visiting the conditional expression, those label-based
// macro conditionals will be able to find them through normal
// label lookups.
Binding<LocalLabel> true_binding{&LabelBindingsManager::Get(), kTrueLabelName,
LocalLabel{true_block}};
Binding<LocalLabel> false_binding{&LabelBindingsManager::Get(),
kFalseLabelName, LocalLabel{false_block}};
StackScope stack_scope(this);
VisitResult expression_result = generator();
if (!expression_result.type()->IsNever()) {
expression_result = stack_scope.Yield(
GenerateImplicitConvert(TypeOracle::GetBoolType(), expression_result));
GenerateBranch(expression_result, true_block, false_block);
}
VisitResult ImplementationVisitor::GenerateBoolConstant(bool constant) {
return GenerateImplicitConvert(TypeOracle::GetBoolType(),
VisitResult(TypeOracle::GetConstexprBoolType(),
constant ? "true" : "false"));
}
void ImplementationVisitor::GenerateExpressionBranch(Expression* expression,
Block* true_block,
Block* false_block) {
GenerateExpressionBranch([&]() { return this->Visit(expression); },
true_block, false_block);
StackScope stack_scope(this);
VisitResult expression_result = this->Visit(expression);
expression_result = stack_scope.Yield(
GenerateImplicitConvert(TypeOracle::GetBoolType(), expression_result));
GenerateBranch(expression_result, true_block, false_block);
}
VisitResult ImplementationVisitor::GenerateImplicitConvert(
......
......@@ -611,9 +611,8 @@ class ImplementationVisitor {
void GenerateBranch(const VisitResult& condition, Block* true_block,
Block* false_block);
using VisitResultGenerator = std::function<VisitResult()>;
void GenerateExpressionBranch(VisitResultGenerator, Block* true_block,
Block* false_block);
VisitResult GenerateBoolConstant(bool constant);
void GenerateExpressionBranch(Expression* expression, Block* true_block,
Block* false_block);
......
......@@ -543,95 +543,34 @@ namespace test {
check(equal);
}
macro BoolToBranch(x: bool): never
labels Taken, NotTaken {
if (x) {
goto Taken;
} else {
goto NotTaken;
}
}
@export
macro TestOrAnd1(x: bool, y: bool, z: bool): bool {
return BoolToBranch(x) || y && z ? true : false;
}
@export
macro TestOrAnd2(x: bool, y: bool, z: bool): bool {
return x || BoolToBranch(y) && z ? true : false;
}
@export
macro TestOrAnd3(x: bool, y: bool, z: bool): bool {
return x || y && BoolToBranch(z) ? true : false;
}
@export
macro TestAndOr1(x: bool, y: bool, z: bool): bool {
return BoolToBranch(x) && y || z ? true : false;
}
@export
macro TestAndOr2(x: bool, y: bool, z: bool): bool {
return x && BoolToBranch(y) || z ? true : false;
macro TestOrAnd(x: bool, y: bool, z: bool): bool {
return x || y && z ? true : false;
}
@export
macro TestAndOr3(x: bool, y: bool, z: bool): bool {
return x && y || BoolToBranch(z) ? true : false;
macro TestAndOr(x: bool, y: bool, z: bool): bool {
return x && y || z ? true : false;
}
@export
macro TestLogicalOperators() {
check(TestAndOr1(true, true, true));
check(TestAndOr2(true, true, true));
check(TestAndOr3(true, true, true));
check(TestAndOr1(true, true, false));
check(TestAndOr2(true, true, false));
check(TestAndOr3(true, true, false));
check(TestAndOr1(true, false, true));
check(TestAndOr2(true, false, true));
check(TestAndOr3(true, false, true));
check(!TestAndOr1(true, false, false));
check(!TestAndOr2(true, false, false));
check(!TestAndOr3(true, false, false));
check(TestAndOr1(false, true, true));
check(TestAndOr2(false, true, true));
check(TestAndOr3(false, true, true));
check(!TestAndOr1(false, true, false));
check(!TestAndOr2(false, true, false));
check(!TestAndOr3(false, true, false));
check(TestAndOr1(false, false, true));
check(TestAndOr2(false, false, true));
check(TestAndOr3(false, false, true));
check(!TestAndOr1(false, false, false));
check(!TestAndOr2(false, false, false));
check(!TestAndOr3(false, false, false));
check(TestOrAnd1(true, true, true));
check(TestOrAnd2(true, true, true));
check(TestOrAnd3(true, true, true));
check(TestOrAnd1(true, true, false));
check(TestOrAnd2(true, true, false));
check(TestOrAnd3(true, true, false));
check(TestOrAnd1(true, false, true));
check(TestOrAnd2(true, false, true));
check(TestOrAnd3(true, false, true));
check(TestOrAnd1(true, false, false));
check(TestOrAnd2(true, false, false));
check(TestOrAnd3(true, false, false));
check(TestOrAnd1(false, true, true));
check(TestOrAnd2(false, true, true));
check(TestOrAnd3(false, true, true));
check(!TestOrAnd1(false, true, false));
check(!TestOrAnd2(false, true, false));
check(!TestOrAnd3(false, true, false));
check(!TestOrAnd1(false, false, true));
check(!TestOrAnd2(false, false, true));
check(!TestOrAnd3(false, false, true));
check(!TestOrAnd1(false, false, false));
check(!TestOrAnd2(false, false, false));
check(!TestOrAnd3(false, false, false));
check(TestAndOr(true, true, true));
check(TestAndOr(true, true, false));
check(TestAndOr(true, false, true));
check(!TestAndOr(true, false, false));
check(TestAndOr(false, true, true));
check(!TestAndOr(false, true, false));
check(TestAndOr(false, false, true));
check(!TestAndOr(false, false, false));
check(TestOrAnd(true, true, true));
check(TestOrAnd(true, true, false));
check(TestOrAnd(true, false, true));
check(TestOrAnd(true, false, false));
check(TestOrAnd(false, true, true));
check(!TestOrAnd(false, true, false));
check(!TestOrAnd(false, false, true));
check(!TestOrAnd(false, false, false));
}
@export
......
......@@ -143,7 +143,7 @@ namespace array {
let canUseSameAccessorFn: CanUseSameAccessorFn;
try {
GotoIfForceSlowPath() otherwise Slow;
if (IsForceSlowPath()) goto Slow;
const a: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
// Copy copy-on-write (COW) arrays.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment