v8/test/torque/test-torque.tq
Seth Brenith 98438d8619 [torque] Generate better code when using & operator on bitfields
Sometimes CSA code carefully constructs a mask to check several
bitfields at once. Thus far, such a check has been very awkward to write
in Torque. This change adds a way to do so, using the
non-short-circuiting binary `&` operator. So now you can write an
expression that depends on several bitfields from a bitfield struct,
like `x.a == 5 & x.b & !x.c & x.d == 2` (assuming b is a one-bit value),
and it will be reduced to a single mask and equality check. To
demonstrate a usage of this new reduction, this change ports the trivial
macro IsSimpleObjectMap to Torque. I manually verified that the
generated code for the builtin SetDataProperties, which uses that macro,
is unchanged.

Bug: v8:7793
Change-Id: I4a23e0005d738a6699ea0f2a63f9fd67b01e7026
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2183276
Commit-Queue: Seth Brenith <seth.brenith@microsoft.com>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67948}
2020-05-22 21:59:06 +00:00

1269 lines
28 KiB
Plaintext

// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Test line comment
/* Test mulitline
comment
*/
namespace test {
macro ElementsKindTestHelper1(kind: constexpr ElementsKind): bool {
if constexpr (
(kind == ElementsKind::UINT8_ELEMENTS) ||
(kind == ElementsKind::UINT16_ELEMENTS)) {
return true;
} else {
return false;
}
}
macro ElementsKindTestHelper2(kind: constexpr ElementsKind): constexpr bool {
return (
(kind == ElementsKind::UINT8_ELEMENTS) ||
(kind == ElementsKind::UINT16_ELEMENTS));
}
macro LabelTestHelper1(): never
labels Label1 {
goto Label1;
}
macro LabelTestHelper2(): never
labels Label2(Smi) {
goto Label2(42);
}
macro LabelTestHelper3(): never
labels Label3(Oddball, Smi) {
goto Label3(Null, 7);
}
@export
macro TestConstexpr1() {
check(FromConstexpr<bool>(
IsFastElementsKind(ElementsKind::PACKED_SMI_ELEMENTS)));
}
@export
macro TestConstexprIf() {
check(ElementsKindTestHelper1(ElementsKind::UINT8_ELEMENTS));
check(ElementsKindTestHelper1(ElementsKind::UINT16_ELEMENTS));
check(!ElementsKindTestHelper1(ElementsKind::UINT32_ELEMENTS));
}
@export
macro TestConstexprReturn() {
check(FromConstexpr<bool>(
ElementsKindTestHelper2(ElementsKind::UINT8_ELEMENTS)));
check(FromConstexpr<bool>(
ElementsKindTestHelper2(ElementsKind::UINT16_ELEMENTS)));
check(!FromConstexpr<bool>(
ElementsKindTestHelper2(ElementsKind::UINT32_ELEMENTS)));
check(FromConstexpr<bool>(
!ElementsKindTestHelper2(ElementsKind::UINT32_ELEMENTS)));
}
@export
macro TestGotoLabel(): Boolean {
try {
LabelTestHelper1() otherwise Label1;
} label Label1 {
return True;
}
}
@export
macro TestGotoLabelWithOneParameter(): Boolean {
try {
LabelTestHelper2() otherwise Label2;
} label Label2(smi: Smi) {
check(smi == 42);
return True;
}
}
@export
macro TestGotoLabelWithTwoParameters(): Boolean {
try {
LabelTestHelper3() otherwise Label3;
} label Label3(o: Oddball, smi: Smi) {
check(o == Null);
check(smi == 7);
return True;
}
}
builtin GenericBuiltinTest<T: type>(_param: T): JSAny {
return Null;
}
GenericBuiltinTest<JSAny>(param: JSAny): JSAny {
return param;
}
@export
macro TestBuiltinSpecialization() {
check(GenericBuiltinTest<Smi>(0) == Null);
check(GenericBuiltinTest<Smi>(1) == Null);
check(GenericBuiltinTest<JSAny>(Undefined) == Undefined);
check(GenericBuiltinTest<JSAny>(Undefined) == Undefined);
}
macro LabelTestHelper4(flag: constexpr bool): never
labels Label4, Label5 {
if constexpr (flag) {
goto Label4;
} else {
goto Label5;
}
}
macro CallLabelTestHelper4(flag: constexpr bool): bool {
try {
LabelTestHelper4(flag) otherwise Label4, Label5;
} label Label4 {
return true;
} label Label5 {
return false;
}
}
@export
macro TestPartiallyUnusedLabel(): Boolean {
const r1: bool = CallLabelTestHelper4(true);
const r2: bool = CallLabelTestHelper4(false);
if (r1 && !r2) {
return True;
} else {
return False;
}
}
macro GenericMacroTest<T: type>(_param: T): Object {
return Undefined;
}
GenericMacroTest<Object>(param2: Object): Object {
return param2;
}
macro GenericMacroTestWithLabels<T: type>(_param: T): Object
labels _X {
return Undefined;
}
GenericMacroTestWithLabels<Object>(param2: Object): Object
labels Y {
return Cast<Smi>(param2) otherwise Y;
}
@export
macro TestMacroSpecialization() {
try {
const _smi0: Smi = 0;
check(GenericMacroTest<Smi>(0) == Undefined);
check(GenericMacroTest<Smi>(1) == Undefined);
check(GenericMacroTest<Object>(Null) == Null);
check(GenericMacroTest<Object>(False) == False);
check(GenericMacroTest<Object>(True) == True);
check((GenericMacroTestWithLabels<Smi>(0) otherwise Fail) == Undefined);
check((GenericMacroTestWithLabels<Smi>(0) otherwise Fail) == Undefined);
try {
GenericMacroTestWithLabels<Object>(False) otherwise Expected;
} label Expected {}
} label Fail {
unreachable;
}
}
builtin TestHelperPlus1(x: Smi): Smi {
return x + 1;
}
builtin TestHelperPlus2(x: Smi): Smi {
return x + 2;
}
@export
macro TestFunctionPointers(implicit context: Context)(): Boolean {
let fptr: builtin(Smi) => Smi = TestHelperPlus1;
check(fptr(42) == 43);
fptr = TestHelperPlus2;
check(fptr(42) == 44);
return True;
}
@export
macro TestVariableRedeclaration(implicit context: Context)(): Boolean {
let _var1: int31 = FromConstexpr<bool>(42 == 0) ? 0 : 1;
let _var2: int31 = FromConstexpr<bool>(42 == 0) ? 1 : 0;
return True;
}
@export
macro TestTernaryOperator(x: Smi): Smi {
const b: bool = x < 0 ? true : false;
return b ? x - 10 : x + 100;
}
@export
macro TestFunctionPointerToGeneric() {
const fptr1: builtin(Smi) => JSAny = GenericBuiltinTest<Smi>;
const fptr2: builtin(JSAny) => JSAny = GenericBuiltinTest<JSAny>;
check(fptr1(0) == Null);
check(fptr1(1) == Null);
check(fptr2(Undefined) == Undefined);
check(fptr2(Undefined) == Undefined);
}
type ObjectToObject = builtin(Context, JSAny) => JSAny;
@export
macro TestTypeAlias(x: ObjectToObject): BuiltinPtr {
return x;
}
@export
macro TestUnsafeCast(implicit context: Context)(n: Number): Boolean {
if (TaggedIsSmi(n)) {
const m: Smi = UnsafeCast<Smi>(n);
check(TestHelperPlus1(m) == 11);
return True;
}
return False;
}
@export
macro TestHexLiteral() {
check(Convert<intptr>(0xffff) + 1 == 0x10000);
check(Convert<intptr>(-0xffff) == -65535);
}
@export
macro TestLargeIntegerLiterals(implicit c: Context)() {
let _x: int32 = 0x40000000;
let _y: int32 = 0x7fffffff;
}
@export
macro TestMultilineAssert() {
const someVeryLongVariableNameThatWillCauseLineBreaks: Smi = 5;
check(
someVeryLongVariableNameThatWillCauseLineBreaks > 0 &&
someVeryLongVariableNameThatWillCauseLineBreaks < 10);
}
@export
macro TestNewlineInString() {
Print('Hello, World!\n');
}
const kConstexprConst: constexpr int31 = 5;
const kIntptrConst: intptr = 4;
const kSmiConst: Smi = 3;
@export
macro TestModuleConstBindings() {
check(kConstexprConst == Int32Constant(5));
check(kIntptrConst == 4);
check(kSmiConst == 3);
}
@export
macro TestLocalConstBindings() {
const x: constexpr int31 = 3;
const xSmi: Smi = x;
{
const x: Smi = x + FromConstexpr<Smi>(1);
check(x == xSmi + 1);
const xSmi: Smi = x;
check(x == xSmi);
check(x == 4);
}
check(xSmi == 3);
check(x == xSmi);
}
struct TestStructA {
indexes: FixedArray;
i: Smi;
k: Number;
}
struct TestStructB {
x: TestStructA;
y: Smi;
}
@export
macro TestStruct1(i: TestStructA): Smi {
return i.i;
}
@export
macro TestStruct2(implicit context: Context)(): TestStructA {
return TestStructA{
indexes: UnsafeCast<FixedArray>(kEmptyFixedArray),
i: 27,
k: 31
};
}
@export
macro TestStruct3(implicit context: Context)(): TestStructA {
let a: TestStructA =
TestStructA{indexes: UnsafeCast<FixedArray>(kEmptyFixedArray), i: 13, k: 5};
let _b: TestStructA = a;
const c: TestStructA = TestStruct2();
a.i = TestStruct1(c);
a.k = a.i;
let d: TestStructB;
d.x = a;
d = TestStructB{x: a, y: 7};
let _e: TestStructA = d.x;
let f: Smi = TestStructA{
indexes: UnsafeCast<FixedArray>(kEmptyFixedArray),
i: 27,
k: 31
}.i;
f = TestStruct2().i;
return a;
}
struct TestStructC {
x: TestStructA;
y: TestStructA;
}
@export
macro TestStruct4(implicit context: Context)(): TestStructC {
return TestStructC{x: TestStruct2(), y: TestStruct2()};
}
macro TestStructInLabel(implicit context: Context)(): never labels
Foo(TestStructA) {
goto Foo(TestStruct2());
}
@export // Silence unused warning.
macro CallTestStructInLabel(implicit context: Context)() {
try {
TestStructInLabel() otherwise Foo;
} label Foo(_s: TestStructA) {}
}
// This macro tests different versions of the for-loop where some parts
// are (not) present.
@export
macro TestForLoop() {
let sum: Smi = 0;
for (let i: Smi = 0; i < 5; ++i) sum += i;
check(sum == 10);
sum = 0;
let j: Smi = 0;
for (; j < 5; ++j) sum += j;
check(sum == 10);
sum = 0;
j = 0;
for (; j < 5;) sum += j++;
check(sum == 10);
// Check that break works. No test expression.
sum = 0;
for (let i: Smi = 0;; ++i) {
if (i == 5) break;
sum += i;
}
check(sum == 10);
sum = 0;
j = 0;
for (;;) {
if (j == 5) break;
sum += j;
j++;
}
check(sum == 10);
// The following tests are the same as above, but use continue to skip
// index 3.
sum = 0;
for (let i: Smi = 0; i < 5; ++i) {
if (i == 3) continue;
sum += i;
}
check(sum == 7);
sum = 0;
j = 0;
for (; j < 5; ++j) {
if (j == 3) continue;
sum += j;
}
check(sum == 7);
sum = 0;
j = 0;
for (; j < 5;) {
if (j == 3) {
j++;
continue;
}
sum += j;
j++;
}
check(sum == 7);
sum = 0;
for (let i: Smi = 0;; ++i) {
if (i == 3) continue;
if (i == 5) break;
sum += i;
}
check(sum == 7);
sum = 0;
j = 0;
for (;;) {
if (j == 3) {
j++;
continue;
}
if (j == 5) break;
sum += j;
j++;
}
check(sum == 7);
j = 0;
try {
for (;;) {
if (++j == 10) goto Exit;
}
} label Exit {
check(j == 10);
}
// Test if we can handle uninitialized values on the stack.
let _i: Smi;
for (let j: Smi = 0; j < 10; ++j) {
}
}
@export
macro TestSubtyping(x: Smi) {
const _foo: JSAny = x;
}
macro IncrementIfSmi<A: type>(x: A): A {
typeswitch (x) {
case (x: Smi): {
return x + 1;
}
case (o: A): {
return o;
}
}
}
type NumberOrFixedArray = Number|FixedArray;
macro TypeswitchExample(implicit context: Context)(x: NumberOrFixedArray):
int32 {
let result: int32 = 0;
typeswitch (IncrementIfSmi(x)) {
case (_x: FixedArray): {
result = result + 1;
}
case (Number): {
result = result + 2;
}
}
result = result * 10;
typeswitch (IncrementIfSmi(x)) {
case (x: Smi): {
result = result + Convert<int32>(x);
}
case (a: FixedArray): {
result = result + Convert<int32>(a.length);
}
case (_x: HeapNumber): {
result = result + 7;
}
}
return result;
}
@export
macro TestTypeswitch(implicit context: Context)() {
check(TypeswitchExample(FromConstexpr<Smi>(5)) == 26);
const a: FixedArray = AllocateZeroedFixedArray(3);
check(TypeswitchExample(a) == 13);
check(TypeswitchExample(FromConstexpr<Number>(0.5)) == 27);
}
@export
macro TestTypeswitchAsanLsanFailure(implicit context: Context)(obj: Object) {
typeswitch (obj) {
case (_o: Smi): {
}
case (_o: JSTypedArray): {
}
case (_o: JSReceiver): {
}
case (_o: HeapObject): {
}
}
}
macro ExampleGenericOverload<A: type>(o: Object): A {
return o;
}
macro ExampleGenericOverload<A: type>(o: Smi): A {
return o + 1;
}
@export
macro TestGenericOverload(implicit context: Context)() {
const xSmi: Smi = 5;
const xObject: Object = xSmi;
check(ExampleGenericOverload<Smi>(xSmi) == 6);
check(UnsafeCast<Smi>(ExampleGenericOverload<Object>(xObject)) == 5);
}
@export
macro TestEquality(implicit context: Context)() {
const notEqual: bool =
AllocateHeapNumberWithValue(0.5) != AllocateHeapNumberWithValue(0.5);
check(!notEqual);
const equal: bool =
AllocateHeapNumberWithValue(0.5) == AllocateHeapNumberWithValue(0.5);
check(equal);
}
@export
macro TestOrAnd(x: bool, y: bool, z: bool): bool {
return x || y && z ? true : false;
}
@export
macro TestAndOr(x: bool, y: bool, z: bool): bool {
return x && y || z ? true : false;
}
@export
macro TestLogicalOperators() {
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
macro TestCall(i: Smi): Smi labels A {
if (i < 5) return i;
goto A;
}
@export
macro TestOtherwiseWithCode1() {
let v: Smi = 0;
let s: Smi = 1;
try {
TestCall(10) otherwise goto B(++s);
} label B(v1: Smi) {
v = v1;
}
assert(v == 2);
}
@export
macro TestOtherwiseWithCode2() {
let s: Smi = 0;
for (let i: Smi = 0; i < 10; ++i) {
TestCall(i) otherwise break;
++s;
}
assert(s == 5);
}
@export
macro TestOtherwiseWithCode3() {
let s: Smi = 0;
for (let i: Smi = 0; i < 10; ++i) {
s += TestCall(i) otherwise break;
}
assert(s == 10);
}
@export
macro TestForwardLabel() {
try {
goto A;
} label A {
goto B(5);
} label B(b: Smi) {
assert(b == 5);
}
}
@export
macro TestQualifiedAccess(implicit context: Context)() {
const s: Smi = 0;
check(!Is<JSArray>(s));
}
@export
macro TestCatch1(implicit context: Context)(): Smi {
let r: Smi = 0;
try {
ThrowTypeError(MessageTemplate::kInvalidArrayLength);
} catch (_e) {
r = 1;
return r;
}
}
@export
macro TestCatch2Wrapper(implicit context: Context)(): never {
ThrowTypeError(MessageTemplate::kInvalidArrayLength);
}
@export
macro TestCatch2(implicit context: Context)(): Smi {
let r: Smi = 0;
try {
TestCatch2Wrapper();
} catch (_e) {
r = 2;
return r;
}
}
@export
macro TestCatch3WrapperWithLabel(implicit context: Context)():
never labels _Abort {
ThrowTypeError(MessageTemplate::kInvalidArrayLength);
}
@export
macro TestCatch3(implicit context: Context)(): Smi {
let r: Smi = 0;
try {
TestCatch3WrapperWithLabel() otherwise Abort;
} catch (_e) {
r = 2;
return r;
} label Abort {
return -1;
}
}
// This test doesn't actually test the functionality of iterators,
// it's only purpose is to make sure tha the CSA macros in the
// IteratorBuiltinsAssembler match the signatures provided in
// iterator.tq.
@export
transitioning macro TestIterator(implicit context: Context)(
o: JSReceiver, map: Map) {
try {
const t1: JSAny = iterator::GetIteratorMethod(o);
const t2: iterator::IteratorRecord = iterator::GetIterator(o);
const _t3: JSAny = iterator::IteratorStep(t2) otherwise Fail;
const _t4: JSAny = iterator::IteratorStep(t2, map) otherwise Fail;
const _t5: JSAny = iterator::IteratorValue(o);
const _t6: JSAny = iterator::IteratorValue(o, map);
const _t7: JSArray = iterator::IterableToList(t1, t1);
iterator::IteratorCloseOnException(t2);
} label Fail {}
}
@export
macro TestFrame1(implicit context: Context)() {
const f: Frame = LoadFramePointer();
const frameType: FrameType =
Cast<FrameType>(f.context_or_frame_type) otherwise unreachable;
assert(frameType == STUB_FRAME);
assert(f.caller == LoadParentFramePointer());
typeswitch (f) {
case (_f: StandardFrame): {
unreachable;
}
case (_f: ArgumentsAdaptorFrame): {
unreachable;
}
case (_f: StubFrame): {
}
}
}
@export
macro TestNew(implicit context: Context)() {
const f: JSArray = NewJSArray();
check(f.IsEmpty());
f.length = 0;
}
struct TestInner {
macro SetX(newValue: int32) {
this.x = newValue;
}
macro GetX(): int32 {
return this.x;
}
x: int32;
y: int32;
}
struct TestOuter {
a: int32;
b: TestInner;
c: int32;
}
@export
macro TestStructConstructor(implicit context: Context)() {
// Test default constructor
let a: TestOuter = TestOuter{a: 5, b: TestInner{x: 6, y: 7}, c: 8};
check(a.a == 5);
check(a.b.x == 6);
check(a.b.y == 7);
check(a.c == 8);
a.b.x = 1;
check(a.b.x == 1);
a.b.SetX(2);
check(a.b.x == 2);
check(a.b.GetX() == 2);
}
class InternalClass extends HeapObject {
macro Flip() labels NotASmi {
const tmp = Cast<Smi>(this.b) otherwise NotASmi;
this.b = this.a;
this.a = tmp;
}
a: Smi;
b: Number;
}
macro NewInternalClass(x: Smi): InternalClass {
return new InternalClass{a: x, b: x + 1};
}
@export
macro TestInternalClass(implicit context: Context)() {
const o = NewInternalClass(5);
o.Flip() otherwise unreachable;
check(o.a == 6);
check(o.b == 5);
}
struct StructWithConst {
macro TestMethod1(): int32 {
return this.b;
}
macro TestMethod2(): Object {
return this.a;
}
a: Object;
const b: int32;
}
@export
macro TestConstInStructs() {
const x = StructWithConst{a: Null, b: 1};
let y = StructWithConst{a: Null, b: 1};
y.a = Undefined;
const _copy = x;
check(x.TestMethod1() == 1);
check(x.TestMethod2() == Null);
}
@export
macro TestParentFrameArguments(implicit context: Context)() {
const parentFrame = LoadParentFramePointer();
const castFrame = Cast<StandardFrame>(parentFrame) otherwise unreachable;
const arguments = GetFrameArguments(castFrame, 1);
ArgumentsIterator{arguments, current: 0};
}
struct TestIterator {
macro Next(): Object labels NoMore {
if (this.count-- == 0) goto NoMore;
return TheHole;
}
count: Smi;
}
@export
macro TestNewFixedArrayFromSpread(implicit context: Context)(): Object {
let i = TestIterator{count: 5};
return new FixedArray{map: kFixedArrayMap, length: 5, objects: ...i};
}
class SmiPair extends HeapObject {
macro GetA():&Smi {
return & this.a;
}
a: Smi;
b: Smi;
}
macro Swap<T: type>(a:&T, b:&T) {
const tmp = * a;
* a = * b;
* b = tmp;
}
@export
macro TestReferences() {
const array = new SmiPair{a: 7, b: 2};
const ref:&Smi = & array.a;
* ref = 3 + * ref;
-- * ref;
Swap(& array.b, array.GetA());
check(array.a == 2);
check(array.b == 9);
}
@export
macro TestSlices() {
const it = TestIterator{count: 3};
const a = new FixedArray{map: kFixedArrayMap, length: 3, objects: ...it};
check(a.length == 3);
const oneTwoThree = Convert<Smi>(123);
a.objects[0] = oneTwoThree;
const firstRef:&Object = & a.objects[0];
check(TaggedEqual(* firstRef, oneTwoThree));
const slice: torque_internal::Slice<Object> = & a.objects;
const firstRefAgain:&Object = slice.TryAtIndex(0) otherwise unreachable;
check(TaggedEqual(* firstRefAgain, oneTwoThree));
const threeTwoOne = Convert<Smi>(321);
* firstRefAgain = threeTwoOne;
check(TaggedEqual(a.objects[0], threeTwoOne));
// *slice; // error, not allowed
// a.objects; // error, not allowed
// a.objects = slice; // error, not allowed
// TODO(gsps): Currently errors, but should be allowed:
// const _sameSlice: torque_internal::Slice<Object> = &(*slice);
// (*slice)[0] : Smi
}
@export
macro TestSliceEnumeration(implicit context: Context)(): Undefined {
const fixedArray: FixedArray = AllocateZeroedFixedArray(3);
for (let i: intptr = 0; i < 3; i++) {
check(UnsafeCast<Smi>(fixedArray.objects[i]) == 0);
fixedArray.objects[i] = Convert<Smi>(i) + 3;
}
let slice = & fixedArray.objects;
for (let i: intptr = 0; i < slice.length; i++) {
let ref = slice.TryAtIndex(i) otherwise unreachable;
const value = UnsafeCast<Smi>(* ref);
check(value == Convert<Smi>(i) + 3);
* ref = value + 4;
}
let it = slice.Iterator();
let count: Smi = 0;
while (true) {
const value = UnsafeCast<Smi>(it.Next() otherwise break);
check(value == count + 7);
count++;
}
check(count == 3);
check(it.Empty());
return Undefined;
}
@export
macro TestStaticAssert() {
StaticAssert(1 + 2 == 3);
}
class SmiBox extends HeapObject {
value: Smi;
unrelated: Smi;
}
builtin NewSmiBox(implicit context: Context)(value: Smi): SmiBox {
return new SmiBox{value, unrelated: 0};
}
@export
macro TestLoadEliminationFixed(implicit context: Context)() {
const box = NewSmiBox(123);
const v1 = box.value;
box.unrelated = 999;
const v2 = (box.unrelated == 0) ? box.value : box.value;
StaticAssert(TaggedEqual(v1, v2));
box.value = 11;
const v3 = box.value;
const eleven: Smi = 11;
StaticAssert(TaggedEqual(v3, eleven));
}
@export
macro TestLoadEliminationVariable(implicit context: Context)() {
const a = UnsafeCast<FixedArray>(kEmptyFixedArray);
const box = NewSmiBox(1);
const v1 = a.objects[box.value];
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));
}
@export
macro TestRedundantArrayElementCheck(implicit context: Context)(): Smi {
const a = kEmptyFixedArray;
for (let i: Smi = 0; i < a.length; i++) {
if (a.objects[i] == TheHole) {
if (a.objects[i] == TheHole) {
return -1;
} else {
StaticAssert(false);
}
}
}
return 1;
}
@export
macro TestRedundantSmiCheck(implicit context: Context)(): Smi {
const a = kEmptyFixedArray;
const x = a.objects[1];
typeswitch (x) {
case (Smi): {
Cast<Smi>(x) otherwise VerifiedUnreachable();
return -1;
}
case (Object): {
}
}
return 1;
}
struct SBox<T: type> {
value: T;
}
@export
macro TestGenericStruct1(): intptr {
const i: intptr = 123;
let box = SBox{value: i};
let boxbox: SBox<SBox<intptr>> = SBox{value: box};
check(box.value == 123);
boxbox.value.value *= 2;
check(boxbox.value.value == 246);
return boxbox.value.value;
}
struct TestTuple<T1: type, T2: type> {
const fst: T1;
const snd: T2;
}
macro TupleSwap<T1: type, T2: type>(tuple: TestTuple<T1, T2>):
TestTuple<T2, T1> {
return TestTuple{fst: tuple.snd, snd: tuple.fst};
}
@export
macro TestGenericStruct2():
TestTuple<TestTuple<intptr, Smi>, TestTuple<Smi, intptr>> {
const intptrAndSmi = TestTuple<intptr, Smi>{fst: 1, snd: 2};
const smiAndIntptr = TupleSwap(intptrAndSmi);
check(intptrAndSmi.fst == smiAndIntptr.snd);
check(intptrAndSmi.snd == smiAndIntptr.fst);
const tupleTuple =
TestTuple<TestTuple<intptr, Smi>>{fst: intptrAndSmi, snd: smiAndIntptr};
return tupleTuple;
}
macro BranchAndWriteResult(x: Smi, box: SmiBox): bool {
if (x > 5 || x < 0) {
box.value = 1;
return true;
} else {
box.value = 2;
return false;
}
}
@export
macro TestBranchOnBoolOptimization(implicit context: Context)(input: Smi) {
const box = NewSmiBox(1);
// 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);
} else {
StaticAssert(box.value == 2);
}
}
bitfield struct TestBitFieldStruct extends uint8 {
a: bool: 1 bit;
b: uint16: 3 bit;
c: uint32: 3 bit;
d: bool: 1 bit;
}
@export
macro TestBitFieldLoad(
val: TestBitFieldStruct, expectedA: bool, expectedB: uint16,
expectedC: uint32, expectedD: bool) {
check(val.a == expectedA);
check(val.b == expectedB);
check(val.c == expectedC);
check(val.d == expectedD);
}
@export
macro TestBitFieldStore(val: TestBitFieldStruct) {
let val: TestBitFieldStruct = val; // Get a mutable local copy.
const a: bool = val.a;
const b: uint16 = val.b;
let c: uint32 = val.c;
const d: bool = val.d;
val.a = !a;
TestBitFieldLoad(val, !a, b, c, d);
c = Unsigned(7 - Signed(val.c));
val.c = c;
TestBitFieldLoad(val, !a, b, c, d);
val.d = val.b == val.c;
TestBitFieldLoad(val, !a, b, c, b == c);
}
@export
macro TestBitFieldInit(a: bool, b: uint16, c: uint32, d: bool) {
const val: TestBitFieldStruct = TestBitFieldStruct{a: a, b: b, c: c, d: d};
TestBitFieldLoad(val, a, b, c, d);
}
// Some other bitfield structs, to verify getting uintptr values out of word32
// structs and vice versa.
bitfield struct TestBitFieldStruct2 extends uint32 {
a: uintptr: 5 bit;
b: uintptr: 6 bit;
}
bitfield struct TestBitFieldStruct3 extends uintptr {
c: bool: 1 bit;
d: uint32: 9 bit;
e: uintptr: 17 bit;
}
@export
macro TestBitFieldUintptrOps(
val2: TestBitFieldStruct2, val3: TestBitFieldStruct3) {
let val2: TestBitFieldStruct2 = val2; // Get a mutable local copy.
let val3: TestBitFieldStruct3 = val3; // Get a mutable local copy.
// Caller is expected to provide these exact values, so we can verify
// reading values before starting to write anything.
check(val2.a == 3);
check(val2.b == 61);
check(val3.c);
check(val3.d == 500);
check(val3.e == 0x1cc);
val2.b = 16;
check(val2.a == 3);
check(val2.b == 16);
val2.b++;
check(val2.a == 3);
check(val2.b == 17);
val3.d = 99;
val3.e = 1234;
check(val3.c);
check(val3.d == 99);
check(val3.e == 1234);
}
bitfield struct TestBitFieldStruct4 extends uint31 {
a: bool: 1 bit;
b: int32: 3 bit;
c: bool: 1 bit;
}
bitfield struct TestBitFieldStruct5 extends uint31 {
b: int32: 19 bit;
a: bool: 1 bit;
c: bool: 1 bit;
}
@export
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);
simpleExpression = !f.a & f.b == 4 & f.c;
expectedReduction = (Signed(f) & 0x1f) == Convert<int32>(4 << 1 | 1 << 4);
StaticAssert(simpleExpression == expectedReduction);
simpleExpression = f.b == 0 & f.c;
expectedReduction = (Signed(f) & 0x1e) == Convert<int32>(1 << 4);
StaticAssert(simpleExpression == expectedReduction);
simpleExpression = f.a & f.c;
expectedReduction = (Signed(f) & 0x11) == Convert<int32>(1 | 1 << 4);
StaticAssert(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);
simpleExpression = !f2.a & !f2.c;
expectedReduction = (Signed(f2) & 0x180000) == Convert<int32>(0);
StaticAssert(simpleExpression == expectedReduction);
}
@export
class ExportedSubClass extends ExportedSubClassBase {
c_field: int32;
d_field: int32;
e_field: Smi;
}
@export
class ExportedSubClassBase extends HeapObject {
a: HeapObject;
b: HeapObject;
}
@abstract
class AbstractInternalClass extends HeapObject {
}
class AbstractInternalClassSubclass1 extends AbstractInternalClass {}
class AbstractInternalClassSubclass2 extends AbstractInternalClass {}
class InternalClassWithSmiElements extends FixedArrayBase {
data: Smi;
object: Oddball;
entries[length]: Smi;
}
struct InternalClassStructElement {
a: Smi;
b: Smi;
}
class InternalClassWithStructElements extends HeapObject {
dummy1: int32;
dummy2: int32;
const count: Smi;
data: Smi;
object: Object;
entries[count]: Smi;
more_entries[count]: InternalClassStructElement;
}
struct SmiGeneratorIterator {
macro Next(): Smi labels _NoMore {
return this.value++;
}
value: Smi;
}
struct InternalClassStructElementGeneratorIterator {
macro Next(): InternalClassStructElement labels _NoMore {
return InternalClassStructElement{a: this.value++, b: this.value++};
}
value: Smi;
}
@export
macro TestFullyGeneratedClassWithElements() {
// Test creation, initialization and access of a fully generated class with
// simple (Smi) elements
const length: Smi = Convert<Smi>(3);
const object1 = new InternalClassWithSmiElements{
length,
data: 0,
object: Undefined,
entries: ...SmiGeneratorIterator {
value: 11
}
};
assert(object1.length == 3);
assert(object1.data == 0);
assert(object1.object == Undefined);
assert(object1.entries[0] == 11);
assert(object1.entries[1] == 12);
assert(object1.entries[2] == 13);
// Test creation, initialization and access of a fully generated class
// with elements that are a struct.
const object2 = new InternalClassWithStructElements{
dummy1: 44,
dummy2: 45,
count: length,
data: 55,
object: Undefined,
entries: ...SmiGeneratorIterator{value: 3},
more_entries: ...InternalClassStructElementGeneratorIterator {
value: 1
}
};
assert(object2.dummy1 == 44);
assert(object2.dummy2 == 45);
assert(object2.count == 3);
assert(object2.data == 55);
assert(object2.object == Undefined);
assert(object2.entries[0] == 3);
assert(object2.entries[1] == 4);
assert(object2.entries[2] == 5);
assert(object2.more_entries[0].a == 1);
assert(object2.more_entries[0].b == 2);
assert(object2.more_entries[1].a == 3);
assert(object2.more_entries[1].b == 4);
assert(object2.more_entries[2].a == 5);
assert(object2.more_entries[2].b == 6);
}
@export
macro TestFullyGeneratedClassFromCpp(): ExportedSubClass {
return new
ExportedSubClass{a: Null, b: Null, c_field: 7, d_field: 8, e_field: 9};
}
}