From 28a15326434e2fcb2b6661689321c22271d092f4 Mon Sep 17 00:00:00 2001 From: Seth Brenith Date: Wed, 22 Apr 2020 10:05:44 -0700 Subject: [PATCH] [torque] implement initialization of bitfield structs This change allows Torque code to initialize bitfield structs, using the same syntax as struct initialization. It also moves the definition of the JSPromise flags to Torque as an example usage. Bug: v8:7793 Change-Id: I3d5e49aa22139ffb4b8ea9f308dd36a2d22b2c1b Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2148176 Commit-Queue: Seth Brenith Reviewed-by: Tobias Tebbi Cr-Commit-Position: refs/heads/master@{#67338} --- src/builtins/base.tq | 3 + src/builtins/convert.tq | 4 ++ src/builtins/promise-misc.tq | 16 +++-- src/codegen/code-stub-assembler.cc | 26 ++++++-- src/codegen/code-stub-assembler.h | 33 ++++++---- src/objects/js-promise-inl.h | 4 +- src/objects/js-promise.h | 10 +-- src/objects/js-promise.tq | 26 ++++---- src/objects/objects.cc | 8 +-- src/torque/csa-generator.cc | 3 +- src/torque/implementation-visitor.cc | 94 +++++++++++++++------------- src/torque/implementation-visitor.h | 30 ++++++++- src/torque/instructions.h | 7 ++- src/torque/type-visitor.cc | 10 +-- src/torque/type-visitor.h | 4 +- test/cctest/torque/test-torque.cc | 37 +++++++++++ test/torque/test-torque.tq | 6 ++ 17 files changed, 216 insertions(+), 105 deletions(-) diff --git a/src/builtins/base.tq b/src/builtins/base.tq index 078609257d..c644973059 100644 --- a/src/builtins/base.tq +++ b/src/builtins/base.tq @@ -886,6 +886,9 @@ extern macro SmiUntag(Smi): intptr; macro SmiUntag(value: SmiTagged): T { return %RawDownCast(Unsigned(SmiToInt32(Convert(value)))); } +macro SmiTag(value: T): SmiTagged { + return %RawDownCast>(SmiFromUint32(value)); +} extern macro SmiToInt32(Smi): int32; extern macro TaggedIndexToIntPtr(TaggedIndex): intptr; extern macro IntPtrToTaggedIndex(intptr): TaggedIndex; diff --git a/src/builtins/convert.tq b/src/builtins/convert.tq index ee9be1d411..0950cb679d 100644 --- a/src/builtins/convert.tq +++ b/src/builtins/convert.tq @@ -90,6 +90,10 @@ FromConstexpr( c: constexpr LanguageMode): LanguageModeSmi { return %RawDownCast(SmiConstant(c)); } +FromConstexpr(c: constexpr PromiseState): + PromiseState { + return %RawDownCast(Int32Constant(c)); +} macro Convert(i: From): To { return i; diff --git a/src/builtins/promise-misc.tq b/src/builtins/promise-misc.tq index 57ee309b5e..ee221f547c 100644 --- a/src/builtins/promise-misc.tq +++ b/src/builtins/promise-misc.tq @@ -28,9 +28,13 @@ namespace promise { @export macro PromiseInit(promise: JSPromise): void { - assert(PromiseState::kPending == 0); promise.reactions_or_result = kZero; - promise.flags = 0; + promise.flags = SmiTag(JSPromiseFlags{ + status: PromiseState::kPending, + has_handler: false, + handled_hint: false, + async_task_id: 0 + }); promise_internal::ZeroOutEmbedderOffsets(promise); } @@ -46,7 +50,12 @@ namespace promise { promise.properties_or_hash = kEmptyFixedArray; promise.elements = kEmptyFixedArray; promise.reactions_or_result = kZero; - promise.flags = 0; + promise.flags = SmiTag(JSPromiseFlags{ + status: PromiseState::kPending, + has_handler: false, + handled_hint: false, + async_task_id: 0 + }); return promise; } @@ -111,7 +120,6 @@ namespace promise { transitioning macro NewJSPromise(implicit context: Context)( status: constexpr PromiseState, result: JSAny): JSPromise { assert(status != PromiseState::kPending); - assert(kJSPromiseStatusShift == 0); const instance = InnerNewJSPromise(); instance.reactions_or_result = result; diff --git a/src/codegen/code-stub-assembler.cc b/src/codegen/code-stub-assembler.cc index d61ad16f6e..71576b170a 100644 --- a/src/codegen/code-stub-assembler.cc +++ b/src/codegen/code-stub-assembler.cc @@ -7419,25 +7419,39 @@ TNode CodeStubAssembler::DecodeWord(SloppyTNode word, TNode CodeStubAssembler::UpdateWord32(TNode word, TNode value, - uint32_t shift, uint32_t mask) { + uint32_t shift, uint32_t mask, + bool starts_as_zero) { DCHECK_EQ((mask >> shift) << shift, mask); // Ensure the {value} fits fully in the mask. CSA_ASSERT(this, Uint32LessThanOrEqual(value, Uint32Constant(mask >> shift))); TNode encoded_value = Word32Shl(value, Int32Constant(shift)); - TNode inverted_mask = Int32Constant(~mask); - return Word32Or(Word32And(word, inverted_mask), encoded_value); + TNode masked_word; + if (starts_as_zero) { + CSA_ASSERT(this, Word32Equal(Word32And(word, Int32Constant(~mask)), word)); + masked_word = word; + } else { + masked_word = Word32And(word, Int32Constant(~mask)); + } + return Word32Or(masked_word, encoded_value); } TNode CodeStubAssembler::UpdateWord(TNode word, TNode value, - uint32_t shift, uintptr_t mask) { + uint32_t shift, uintptr_t mask, + bool starts_as_zero) { DCHECK_EQ((mask >> shift) << shift, mask); // Ensure the {value} fits fully in the mask. CSA_ASSERT(this, UintPtrLessThanOrEqual(value, UintPtrConstant(mask >> shift))); TNode encoded_value = WordShl(value, static_cast(shift)); - TNode inverted_mask = IntPtrConstant(~static_cast(mask)); - return WordOr(WordAnd(word, inverted_mask), encoded_value); + TNode masked_word; + if (starts_as_zero) { + CSA_ASSERT(this, WordEqual(WordAnd(word, UintPtrConstant(~mask)), word)); + masked_word = word; + } else { + masked_word = WordAnd(word, UintPtrConstant(~mask)); + } + return WordOr(masked_word, encoded_value); } void CodeStubAssembler::SetCounter(StatsCounter* counter, int value) { diff --git a/src/codegen/code-stub-assembler.h b/src/codegen/code-stub-assembler.h index 2b44f4635b..df80e56693 100644 --- a/src/codegen/code-stub-assembler.h +++ b/src/codegen/code-stub-assembler.h @@ -2827,39 +2827,48 @@ class V8_EXPORT_PRIVATE CodeStubAssembler // Returns a node that contains the updated values of a |BitField|. template - TNode UpdateWord32(TNode word, TNode value) { - return UpdateWord32(word, value, BitField::kShift, BitField::kMask); + TNode UpdateWord32(TNode word, TNode value, + bool starts_as_zero = false) { + return UpdateWord32(word, value, BitField::kShift, BitField::kMask, + starts_as_zero); } // Returns a node that contains the updated values of a |BitField|. template - TNode UpdateWord(TNode word, TNode value) { - return UpdateWord(word, value, BitField::kShift, BitField::kMask); + TNode UpdateWord(TNode word, TNode value, + bool starts_as_zero = false) { + return UpdateWord(word, value, BitField::kShift, BitField::kMask, + starts_as_zero); } // Returns a node that contains the updated values of a |BitField|. template - TNode UpdateWordInWord32(TNode word, - TNode value) { - return UncheckedCast(TruncateIntPtrToInt32( - Signed(UpdateWord(ChangeUint32ToWord(word), value)))); + TNode UpdateWordInWord32(TNode word, TNode value, + bool starts_as_zero = false) { + return UncheckedCast( + TruncateIntPtrToInt32(Signed(UpdateWord( + ChangeUint32ToWord(word), value, starts_as_zero)))); } // Returns a node that contains the updated values of a |BitField|. template - TNode UpdateWord32InWord(TNode word, TNode value) { - return UpdateWord(word, ChangeUint32ToWord(value)); + TNode UpdateWord32InWord(TNode word, TNode value, + bool starts_as_zero = false) { + return UpdateWord(word, ChangeUint32ToWord(value), + starts_as_zero); } // Returns a node that contains the updated {value} inside {word} starting // at {shift} and fitting in {mask}. TNode UpdateWord32(TNode word, TNode value, - uint32_t shift, uint32_t mask); + uint32_t shift, uint32_t mask, + bool starts_as_zero = false); // Returns a node that contains the updated {value} inside {word} starting // at {shift} and fitting in {mask}. TNode UpdateWord(TNode word, TNode value, - uint32_t shift, uintptr_t mask); + uint32_t shift, uintptr_t mask, + bool starts_as_zero = false); // Returns true if any of the |T|'s bits in given |word32| are set. template diff --git a/src/objects/js-promise-inl.h b/src/objects/js-promise-inl.h index cffd10b9f1..601de6612b 100644 --- a/src/objects/js-promise-inl.h +++ b/src/objects/js-promise-inl.h @@ -18,8 +18,8 @@ namespace internal { TQ_OBJECT_CONSTRUCTORS_IMPL(JSPromise) -BOOL_ACCESSORS(JSPromise, flags, has_handler, kHasHandlerBit) -BOOL_ACCESSORS(JSPromise, flags, handled_hint, kHandledHintBit) +BOOL_ACCESSORS(JSPromise, flags, has_handler, HasHandlerBit::kShift) +BOOL_ACCESSORS(JSPromise, flags, handled_hint, HandledHintBit::kShift) Object JSPromise::result() const { DCHECK_NE(Promise::kPending, status()); diff --git a/src/objects/js-promise.h b/src/objects/js-promise.h index efb13dc015..cd54f9349e 100644 --- a/src/objects/js-promise.h +++ b/src/objects/js-promise.h @@ -7,6 +7,7 @@ #include "src/objects/js-objects.h" #include "src/objects/promise.h" +#include "torque-generated/bit-fields-tq.h" // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" @@ -64,15 +65,8 @@ class JSPromise : public TorqueGeneratedJSPromise { kHeaderSize + v8::Promise::kEmbedderFieldCount * kEmbedderDataSlotSize; // Flags layout. - // The first two bits store the v8::Promise::PromiseState. - static const int kStatusBits = 2; - static const int kHasHandlerBit = 2; - static const int kHandledHintBit = 3; - using AsyncTaskIdField = base::BitField; + DEFINE_TORQUE_GENERATED_JS_PROMISE_FLAGS() - static const int kStatusShift = 0; - static const int kStatusMask = 0x3; - static const int kHasHandlerMask = 0x4; STATIC_ASSERT(v8::Promise::kPending == 0); STATIC_ASSERT(v8::Promise::kFulfilled == 1); STATIC_ASSERT(v8::Promise::kRejected == 2); diff --git a/src/objects/js-promise.tq b/src/objects/js-promise.tq index 515dedcb9b..ae1c2bcc9d 100644 --- a/src/objects/js-promise.tq +++ b/src/objects/js-promise.tq @@ -2,40 +2,36 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// JSPromise constants -const kJSPromiseStatusMask: constexpr int31 - generates 'JSPromise::kStatusMask'; -const kJSPromiseStatusShift: constexpr int31 - generates 'JSPromise::kStatusShift'; -const kJSPromiseHasHandlerMask: constexpr int31 - generates 'JSPromise::kHasHandlerMask'; +bitfield struct JSPromiseFlags extends uint31 { + status: PromiseState: 2 bit; + has_handler: bool: 1 bit; + handled_hint: bool: 1 bit; + async_task_id: int32: 22 bit; +} @generateCppClass extern class JSPromise extends JSObject { macro Status(): PromiseState { - StaticAssert(kJSPromiseStatusShift == 0); - const status: int32 = Convert(this.flags) & kJSPromiseStatusMask; - return Convert(status); + return this.flags.status; } macro SetStatus(status: constexpr PromiseState): void { assert(this.Status() == PromiseState::kPending); assert(status != PromiseState::kPending); - const mask: Smi = SmiConstant(status); - this.flags = this.flags | mask; + this.flags.status = status; } macro HasHandler(): bool { - return (this.flags & kJSPromiseHasHandlerMask) != 0; + return this.flags.has_handler; } macro SetHasHandler(): void { - this.flags |= kJSPromiseHasHandlerMask; + this.flags.has_handler = true; } // Smi 0 terminated list of PromiseReaction objects in case the JSPromise was // not settled yet, otherwise the result. reactions_or_result: Zero|PromiseReaction|JSAny; - flags: Smi; + flags: SmiTagged; } diff --git a/src/objects/objects.cc b/src/objects/objects.cc index daedf6b3dd..9e21e33bd9 100644 --- a/src/objects/objects.cc +++ b/src/objects/objects.cc @@ -5876,13 +5876,13 @@ class StringSharedKey : public HashTableKey { }; v8::Promise::PromiseState JSPromise::status() const { - int value = flags() & kStatusMask; + int value = flags() & StatusBits::kMask; DCHECK(value == 0 || value == 1 || value == 2); return static_cast(value); } void JSPromise::set_status(Promise::PromiseState status) { - int value = flags() & ~kStatusMask; + int value = flags() & ~StatusBits::kMask; set_flags(value | status); } @@ -5900,11 +5900,11 @@ const char* JSPromise::Status(v8::Promise::PromiseState status) { } int JSPromise::async_task_id() const { - return AsyncTaskIdField::decode(flags()); + return AsyncTaskIdBits::decode(flags()); } void JSPromise::set_async_task_id(int id) { - set_flags(AsyncTaskIdField::update(flags(), id)); + set_flags(AsyncTaskIdBits::update(flags(), id)); } // static diff --git a/src/torque/csa-generator.cc b/src/torque/csa-generator.cc index f6dd3acbce..8b4d2068d3 100644 --- a/src/torque/csa-generator.cc +++ b/src/torque/csa-generator.cc @@ -957,7 +957,8 @@ void CSAGenerator::EmitInstruction(const StoreBitFieldInstruction& instruction, "CodeStubAssembler(state_)." + encoder + "<" + GetBitFieldSpecialization(struct_type, instruction.bit_field) + ">(ca_.UncheckedCast<" + struct_word_type + ">(" + bit_field_struct + - "), ca_.UncheckedCast<" + field_word_type + ">(" + value + "))"; + "), ca_.UncheckedCast<" + field_word_type + ">(" + value + ")" + + (instruction.starts_as_zero ? ", true" : "") + ")"; if (smi_tagged_type) { result_expression = diff --git a/src/torque/implementation-visitor.cc b/src/torque/implementation-visitor.cc index 486e8dd88a..dad2d8751f 100644 --- a/src/torque/implementation-visitor.cc +++ b/src/torque/implementation-visitor.cc @@ -1192,30 +1192,6 @@ VisitResult ImplementationVisitor::Visit(StatementExpression* expr) { return VisitResult{Visit(expr->statement), assembler().TopRange(0)}; } -void ImplementationVisitor::CheckInitializersWellformed( - const std::string& aggregate_name, - const std::vector& aggregate_fields, - const std::vector& initializers, - bool ignore_first_field) { - size_t fields_offset = ignore_first_field ? 1 : 0; - size_t fields_size = aggregate_fields.size() - fields_offset; - for (size_t i = 0; i < std::min(fields_size, initializers.size()); i++) { - const std::string& field_name = - aggregate_fields[i + fields_offset].name_and_type.name; - Identifier* found_name = initializers[i].name; - if (field_name != found_name->value) { - Error("Expected field name \"", field_name, "\" instead of \"", - found_name->value, "\"") - .Position(found_name->pos) - .Throw(); - } - } - if (fields_size != initializers.size()) { - ReportError("expected ", fields_size, " initializers for ", aggregate_name, - " found ", initializers.size()); - } -} - InitializerResults ImplementationVisitor::VisitInitializerResults( const ClassType* class_type, const std::vector& initializers) { @@ -1924,21 +1900,57 @@ VisitResult ImplementationVisitor::Visit(StructExpression* expr) { } // Compute and check struct type from given struct name and argument types - const StructType* struct_type = TypeVisitor::ComputeTypeForStructExpression( + const Type* type = TypeVisitor::ComputeTypeForStructExpression( expr->type, term_argument_types); - CheckInitializersWellformed(struct_type->name(), struct_type->fields(), - initializers); + if (const auto* struct_type = StructType::DynamicCast(type)) { + CheckInitializersWellformed(struct_type->name(), struct_type->fields(), + initializers); - // Implicitly convert values and thereby build the struct on the stack - StackRange struct_range = assembler().TopRange(0); - auto& fields = struct_type->fields(); - for (size_t i = 0; i < values.size(); i++) { - values[i] = - GenerateImplicitConvert(fields[i].name_and_type.type, values[i]); - struct_range.Extend(values[i].stack_range()); + // Implicitly convert values and thereby build the struct on the stack + StackRange struct_range = assembler().TopRange(0); + auto& fields = struct_type->fields(); + for (size_t i = 0; i < values.size(); i++) { + values[i] = + GenerateImplicitConvert(fields[i].name_and_type.type, values[i]); + struct_range.Extend(values[i].stack_range()); + } + + return stack_scope.Yield(VisitResult(struct_type, struct_range)); + } else { + const auto* bitfield_struct_type = BitFieldStructType::cast(type); + CheckInitializersWellformed(bitfield_struct_type->name(), + bitfield_struct_type->fields(), initializers); + + // Create a zero and cast it to the desired bitfield struct type. + VisitResult result{TypeOracle::GetConstInt32Type(), "0"}; + result = GenerateImplicitConvert(TypeOracle::GetInt32Type(), result); + result = GenerateCall("Unsigned", Arguments{{result}, {}}, {}); + result = GenerateCall("%RawDownCast", Arguments{{result}, {}}, + {bitfield_struct_type}); + + // Set each field in the result. If these fields are constexpr, then all of + // this initialization will end up reduced to a single value during TurboFan + // optimization. + auto& fields = bitfield_struct_type->fields(); + for (size_t i = 0; i < values.size(); i++) { + values[i] = + GenerateImplicitConvert(fields[i].name_and_type.type, values[i]); + result = GenerateSetBitField(bitfield_struct_type, fields[i], result, + values[i], /*starts_as_zero=*/true); + } + + return stack_scope.Yield(result); } +} - return stack_scope.Yield(VisitResult(struct_type, struct_range)); +VisitResult ImplementationVisitor::GenerateSetBitField( + const Type* bitfield_struct_type, const BitField& bitfield, + VisitResult bitfield_struct, VisitResult value, bool starts_as_zero) { + GenerateCopy(bitfield_struct); + GenerateCopy(value); + assembler().Emit( + StoreBitFieldInstruction{bitfield_struct_type, bitfield, starts_as_zero}); + return VisitResult(bitfield_struct_type, assembler().TopRange(1)); } LocationReference ImplementationVisitor::GetLocationReference( @@ -2282,13 +2294,11 @@ void ImplementationVisitor::GenerateAssignToLocation( GenerateFetchFromLocation(reference.bit_field_struct_location()); VisitResult converted_value = GenerateImplicitConvert(reference.ReferencedType(), assignment_value); - GenerateCopy(bit_field_struct); - GenerateCopy(converted_value); - assembler().Emit(StoreBitFieldInstruction{bit_field_struct.type(), - reference.bit_field()}); - GenerateAssignToLocation( - reference.bit_field_struct_location(), - VisitResult(bit_field_struct.type(), assembler().TopRange(1))); + VisitResult updated_bit_field_struct = + GenerateSetBitField(bit_field_struct.type(), reference.bit_field(), + bit_field_struct, converted_value); + GenerateAssignToLocation(reference.bit_field_struct_location(), + updated_bit_field_struct); } else { DCHECK(reference.IsTemporary()); ReportError("cannot assign to const-bound or temporary ", diff --git a/src/torque/implementation-visitor.h b/src/torque/implementation-visitor.h index 0cd58aa922..c980f3d59b 100644 --- a/src/torque/implementation-visitor.h +++ b/src/torque/implementation-visitor.h @@ -432,11 +432,29 @@ class ImplementationVisitor { VisitResult Visit(Expression* expr); const Type* Visit(Statement* stmt); + template void CheckInitializersWellformed( - const std::string& aggregate_name, - const std::vector& aggregate_fields, + const std::string& aggregate_name, const std::vector& aggregate_fields, const std::vector& initializers, - bool ignore_first_field = false); + bool ignore_first_field = false) { + size_t fields_offset = ignore_first_field ? 1 : 0; + size_t fields_size = aggregate_fields.size() - fields_offset; + for (size_t i = 0; i < std::min(fields_size, initializers.size()); i++) { + const std::string& field_name = + aggregate_fields[i + fields_offset].name_and_type.name; + Identifier* found_name = initializers[i].name; + if (field_name != found_name->value) { + Error("Expected field name \"", field_name, "\" instead of \"", + found_name->value, "\"") + .Position(found_name->pos) + .Throw(); + } + } + if (fields_size != initializers.size()) { + ReportError("expected ", fields_size, " initializers for ", + aggregate_name, " found ", initializers.size()); + } + } InitializerResults VisitInitializerResults( const ClassType* class_type, @@ -713,6 +731,12 @@ class ImplementationVisitor { StackRange GenerateLabelGoto(LocalLabel* label, base::Optional arguments = {}); + VisitResult GenerateSetBitField(const Type* bitfield_struct_type, + const BitField& bitfield, + VisitResult bitfield_struct, + VisitResult value, + bool starts_as_zero = false); + std::vector*> LabelsFromIdentifiers( const std::vector& names); diff --git a/src/torque/instructions.h b/src/torque/instructions.h index d6526d0a7c..4609e8c223 100644 --- a/src/torque/instructions.h +++ b/src/torque/instructions.h @@ -364,14 +364,17 @@ struct LoadBitFieldInstruction : InstructionBase { struct StoreBitFieldInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() StoreBitFieldInstruction(const Type* bit_field_struct_type, - BitField bit_field) + BitField bit_field, bool starts_as_zero) : bit_field_struct_type(bit_field_struct_type), - bit_field(std::move(bit_field)) {} + bit_field(std::move(bit_field)), + starts_as_zero(starts_as_zero) {} DefinitionLocation GetValueDefinition() const; const Type* bit_field_struct_type; BitField bit_field; + // Allows skipping the mask step if we know the starting value is zero. + bool starts_as_zero; }; struct CallIntrinsicInstruction : InstructionBase { diff --git a/src/torque/type-visitor.cc b/src/torque/type-visitor.cc index fc1adb19f6..5b61baf3a8 100644 --- a/src/torque/type-visitor.cc +++ b/src/torque/type-visitor.cc @@ -452,7 +452,7 @@ void TypeVisitor::VisitStructMethods( DeclareMethods(struct_type, struct_declaration->methods); } -const StructType* TypeVisitor::ComputeTypeForStructExpression( +const Type* TypeVisitor::ComputeTypeForStructExpression( TypeExpression* type_expression, const std::vector& term_argument_types) { auto* basic = BasicTypeExpression::DynamicCast(type_expression); @@ -472,11 +472,11 @@ const StructType* TypeVisitor::ComputeTypeForStructExpression( // Compute types of non-generic structs as usual if (!(maybe_generic_type && decl)) { const Type* type = ComputeType(type_expression); - const StructType* struct_type = StructType::DynamicCast(type); - if (!struct_type) { - ReportError(*type, " is not a struct, but used like one"); + if (!type->IsStructType() && !type->IsBitFieldStructType()) { + ReportError(*type, + " is not a struct or bitfield struct, but used like one"); } - return struct_type; + return type; } auto generic_type = *maybe_generic_type; diff --git a/src/torque/type-visitor.h b/src/torque/type-visitor.h index 6bb2bba499..205e842cc7 100644 --- a/src/torque/type-visitor.h +++ b/src/torque/type-visitor.h @@ -32,7 +32,9 @@ class TypeVisitor { static void VisitStructMethods(StructType* struct_type, const StructDeclaration* struct_declaration); static Signature MakeSignature(const CallableDeclaration* declaration); - static const StructType* ComputeTypeForStructExpression( + // Can return either StructType or BitFieldStructType, since they can both be + // used in struct expressions like `MyStruct{ a: 0, b: foo }` + static const Type* ComputeTypeForStructExpression( TypeExpression* type_expression, const std::vector& term_argument_types); diff --git a/test/cctest/torque/test-torque.cc b/test/cctest/torque/test-torque.cc index 9442dab6f5..54bff6f43c 100644 --- a/test/cctest/torque/test-torque.cc +++ b/test/cctest/torque/test-torque.cc @@ -694,6 +694,43 @@ TEST(TestBitFieldStore) { } } +TEST(TestBitFieldInit) { + CcTest::InitializeVM(); + Isolate* isolate(CcTest::i_isolate()); + i::HandleScope scope(isolate); + const int kNumParams = 4; + CodeAssemblerTester asm_tester(isolate, kNumParams); + TestTorqueAssembler m(asm_tester.state()); + { + // Untag all of the parameters to get plain integer values. + TNode a = + m.UncheckedCast(m.Unsigned(m.SmiToInt32(m.Parameter(0)))); + TNode b = + m.UncheckedCast(m.Unsigned(m.SmiToInt32(m.Parameter(1)))); + TNode c = + m.UncheckedCast(m.Unsigned(m.SmiToInt32(m.Parameter(2)))); + TNode d = + m.UncheckedCast(m.Unsigned(m.SmiToInt32(m.Parameter(3)))); + + // Call the Torque-defined macro, which verifies that reading each bitfield + // out of val yields the correct result. + m.TestBitFieldInit(a, b, c, d); + m.Return(m.UndefinedConstant()); + } + FunctionTester ft(asm_tester.GenerateCode(), kNumParams); + + // Test every possible bit combination for this 8-bit value. + for (int a = 0; a <= 1; ++a) { + for (int b = 0; b <= 7; ++b) { + for (int c = 0; c <= 7; ++c) { + for (int d = 0; d <= 1; ++d) { + ft.Call(ft.Val(a), ft.Val(b), ft.Val(c), ft.Val(d)); + } + } + } + } +} + TEST(TestBitFieldUintptrOps) { CcTest::InitializeVM(); Isolate* isolate(CcTest::i_isolate()); diff --git a/test/torque/test-torque.tq b/test/torque/test-torque.tq index a1c1199856..7d9f5c01f5 100644 --- a/test/torque/test-torque.tq +++ b/test/torque/test-torque.tq @@ -1085,6 +1085,12 @@ namespace test { 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 {