[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 <seth.brenith@microsoft.com>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67338}
This commit is contained in:
Seth Brenith 2020-04-22 10:05:44 -07:00 committed by Commit Bot
parent c948f08f9b
commit 28a1532643
17 changed files with 216 additions and 105 deletions

View File

@ -886,6 +886,9 @@ extern macro SmiUntag(Smi): intptr;
macro SmiUntag<T: type>(value: SmiTagged<T>): T { macro SmiUntag<T: type>(value: SmiTagged<T>): T {
return %RawDownCast<T>(Unsigned(SmiToInt32(Convert<Smi>(value)))); return %RawDownCast<T>(Unsigned(SmiToInt32(Convert<Smi>(value))));
} }
macro SmiTag<T : type extends uint31>(value: T): SmiTagged<T> {
return %RawDownCast<SmiTagged<T>>(SmiFromUint32(value));
}
extern macro SmiToInt32(Smi): int32; extern macro SmiToInt32(Smi): int32;
extern macro TaggedIndexToIntPtr(TaggedIndex): intptr; extern macro TaggedIndexToIntPtr(TaggedIndex): intptr;
extern macro IntPtrToTaggedIndex(intptr): TaggedIndex; extern macro IntPtrToTaggedIndex(intptr): TaggedIndex;

View File

@ -90,6 +90,10 @@ FromConstexpr<LanguageModeSmi, constexpr LanguageMode>(
c: constexpr LanguageMode): LanguageModeSmi { c: constexpr LanguageMode): LanguageModeSmi {
return %RawDownCast<LanguageModeSmi>(SmiConstant(c)); return %RawDownCast<LanguageModeSmi>(SmiConstant(c));
} }
FromConstexpr<PromiseState, constexpr PromiseState>(c: constexpr PromiseState):
PromiseState {
return %RawDownCast<PromiseState>(Int32Constant(c));
}
macro Convert<To: type, From: type>(i: From): To { macro Convert<To: type, From: type>(i: From): To {
return i; return i;

View File

@ -28,9 +28,13 @@ namespace promise {
@export @export
macro PromiseInit(promise: JSPromise): void { macro PromiseInit(promise: JSPromise): void {
assert(PromiseState::kPending == 0);
promise.reactions_or_result = kZero; 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); promise_internal::ZeroOutEmbedderOffsets(promise);
} }
@ -46,7 +50,12 @@ namespace promise {
promise.properties_or_hash = kEmptyFixedArray; promise.properties_or_hash = kEmptyFixedArray;
promise.elements = kEmptyFixedArray; promise.elements = kEmptyFixedArray;
promise.reactions_or_result = kZero; 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; return promise;
} }
@ -111,7 +120,6 @@ namespace promise {
transitioning macro NewJSPromise(implicit context: Context)( transitioning macro NewJSPromise(implicit context: Context)(
status: constexpr PromiseState, result: JSAny): JSPromise { status: constexpr PromiseState, result: JSAny): JSPromise {
assert(status != PromiseState::kPending); assert(status != PromiseState::kPending);
assert(kJSPromiseStatusShift == 0);
const instance = InnerNewJSPromise(); const instance = InnerNewJSPromise();
instance.reactions_or_result = result; instance.reactions_or_result = result;

View File

@ -7419,25 +7419,39 @@ TNode<UintPtrT> CodeStubAssembler::DecodeWord(SloppyTNode<WordT> word,
TNode<Word32T> CodeStubAssembler::UpdateWord32(TNode<Word32T> word, TNode<Word32T> CodeStubAssembler::UpdateWord32(TNode<Word32T> word,
TNode<Uint32T> value, TNode<Uint32T> value,
uint32_t shift, uint32_t mask) { uint32_t shift, uint32_t mask,
bool starts_as_zero) {
DCHECK_EQ((mask >> shift) << shift, mask); DCHECK_EQ((mask >> shift) << shift, mask);
// Ensure the {value} fits fully in the mask. // Ensure the {value} fits fully in the mask.
CSA_ASSERT(this, Uint32LessThanOrEqual(value, Uint32Constant(mask >> shift))); CSA_ASSERT(this, Uint32LessThanOrEqual(value, Uint32Constant(mask >> shift)));
TNode<Word32T> encoded_value = Word32Shl(value, Int32Constant(shift)); TNode<Word32T> encoded_value = Word32Shl(value, Int32Constant(shift));
TNode<Word32T> inverted_mask = Int32Constant(~mask); TNode<Word32T> masked_word;
return Word32Or(Word32And(word, inverted_mask), encoded_value); 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<WordT> CodeStubAssembler::UpdateWord(TNode<WordT> word, TNode<WordT> CodeStubAssembler::UpdateWord(TNode<WordT> word,
TNode<UintPtrT> value, TNode<UintPtrT> value,
uint32_t shift, uintptr_t mask) { uint32_t shift, uintptr_t mask,
bool starts_as_zero) {
DCHECK_EQ((mask >> shift) << shift, mask); DCHECK_EQ((mask >> shift) << shift, mask);
// Ensure the {value} fits fully in the mask. // Ensure the {value} fits fully in the mask.
CSA_ASSERT(this, CSA_ASSERT(this,
UintPtrLessThanOrEqual(value, UintPtrConstant(mask >> shift))); UintPtrLessThanOrEqual(value, UintPtrConstant(mask >> shift)));
TNode<WordT> encoded_value = WordShl(value, static_cast<int>(shift)); TNode<WordT> encoded_value = WordShl(value, static_cast<int>(shift));
TNode<IntPtrT> inverted_mask = IntPtrConstant(~static_cast<intptr_t>(mask)); TNode<WordT> masked_word;
return WordOr(WordAnd(word, inverted_mask), encoded_value); 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) { void CodeStubAssembler::SetCounter(StatsCounter* counter, int value) {

View File

@ -2827,39 +2827,48 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
// Returns a node that contains the updated values of a |BitField|. // Returns a node that contains the updated values of a |BitField|.
template <typename BitField> template <typename BitField>
TNode<Word32T> UpdateWord32(TNode<Word32T> word, TNode<Uint32T> value) { TNode<Word32T> UpdateWord32(TNode<Word32T> word, TNode<Uint32T> value,
return UpdateWord32(word, value, BitField::kShift, BitField::kMask); 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|. // Returns a node that contains the updated values of a |BitField|.
template <typename BitField> template <typename BitField>
TNode<WordT> UpdateWord(TNode<WordT> word, TNode<UintPtrT> value) { TNode<WordT> UpdateWord(TNode<WordT> word, TNode<UintPtrT> value,
return UpdateWord(word, value, BitField::kShift, BitField::kMask); 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|. // Returns a node that contains the updated values of a |BitField|.
template <typename BitField> template <typename BitField>
TNode<Word32T> UpdateWordInWord32(TNode<Word32T> word, TNode<Word32T> UpdateWordInWord32(TNode<Word32T> word, TNode<UintPtrT> value,
TNode<UintPtrT> value) { bool starts_as_zero = false) {
return UncheckedCast<Uint32T>(TruncateIntPtrToInt32( return UncheckedCast<Uint32T>(
Signed(UpdateWord<BitField>(ChangeUint32ToWord(word), value)))); TruncateIntPtrToInt32(Signed(UpdateWord<BitField>(
ChangeUint32ToWord(word), value, starts_as_zero))));
} }
// Returns a node that contains the updated values of a |BitField|. // Returns a node that contains the updated values of a |BitField|.
template <typename BitField> template <typename BitField>
TNode<WordT> UpdateWord32InWord(TNode<WordT> word, TNode<Uint32T> value) { TNode<WordT> UpdateWord32InWord(TNode<WordT> word, TNode<Uint32T> value,
return UpdateWord<BitField>(word, ChangeUint32ToWord(value)); bool starts_as_zero = false) {
return UpdateWord<BitField>(word, ChangeUint32ToWord(value),
starts_as_zero);
} }
// Returns a node that contains the updated {value} inside {word} starting // Returns a node that contains the updated {value} inside {word} starting
// at {shift} and fitting in {mask}. // at {shift} and fitting in {mask}.
TNode<Word32T> UpdateWord32(TNode<Word32T> word, TNode<Uint32T> value, TNode<Word32T> UpdateWord32(TNode<Word32T> word, TNode<Uint32T> 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 // Returns a node that contains the updated {value} inside {word} starting
// at {shift} and fitting in {mask}. // at {shift} and fitting in {mask}.
TNode<WordT> UpdateWord(TNode<WordT> word, TNode<UintPtrT> value, TNode<WordT> UpdateWord(TNode<WordT> word, TNode<UintPtrT> 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. // Returns true if any of the |T|'s bits in given |word32| are set.
template <typename T> template <typename T>

View File

@ -18,8 +18,8 @@ namespace internal {
TQ_OBJECT_CONSTRUCTORS_IMPL(JSPromise) TQ_OBJECT_CONSTRUCTORS_IMPL(JSPromise)
BOOL_ACCESSORS(JSPromise, flags, has_handler, kHasHandlerBit) BOOL_ACCESSORS(JSPromise, flags, has_handler, HasHandlerBit::kShift)
BOOL_ACCESSORS(JSPromise, flags, handled_hint, kHandledHintBit) BOOL_ACCESSORS(JSPromise, flags, handled_hint, HandledHintBit::kShift)
Object JSPromise::result() const { Object JSPromise::result() const {
DCHECK_NE(Promise::kPending, status()); DCHECK_NE(Promise::kPending, status());

View File

@ -7,6 +7,7 @@
#include "src/objects/js-objects.h" #include "src/objects/js-objects.h"
#include "src/objects/promise.h" #include "src/objects/promise.h"
#include "torque-generated/bit-fields-tq.h"
// Has to be the last include (doesn't have include guards): // Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h" #include "src/objects/object-macros.h"
@ -64,15 +65,8 @@ class JSPromise : public TorqueGeneratedJSPromise<JSPromise, JSObject> {
kHeaderSize + v8::Promise::kEmbedderFieldCount * kEmbedderDataSlotSize; kHeaderSize + v8::Promise::kEmbedderFieldCount * kEmbedderDataSlotSize;
// Flags layout. // Flags layout.
// The first two bits store the v8::Promise::PromiseState. DEFINE_TORQUE_GENERATED_JS_PROMISE_FLAGS()
static const int kStatusBits = 2;
static const int kHasHandlerBit = 2;
static const int kHandledHintBit = 3;
using AsyncTaskIdField = base::BitField<int, kHandledHintBit + 1, 22>;
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::kPending == 0);
STATIC_ASSERT(v8::Promise::kFulfilled == 1); STATIC_ASSERT(v8::Promise::kFulfilled == 1);
STATIC_ASSERT(v8::Promise::kRejected == 2); STATIC_ASSERT(v8::Promise::kRejected == 2);

View File

@ -2,40 +2,36 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// JSPromise constants bitfield struct JSPromiseFlags extends uint31 {
const kJSPromiseStatusMask: constexpr int31 status: PromiseState: 2 bit;
generates 'JSPromise::kStatusMask'; has_handler: bool: 1 bit;
const kJSPromiseStatusShift: constexpr int31 handled_hint: bool: 1 bit;
generates 'JSPromise::kStatusShift'; async_task_id: int32: 22 bit;
const kJSPromiseHasHandlerMask: constexpr int31 }
generates 'JSPromise::kHasHandlerMask';
@generateCppClass @generateCppClass
extern class JSPromise extends JSObject { extern class JSPromise extends JSObject {
macro Status(): PromiseState { macro Status(): PromiseState {
StaticAssert(kJSPromiseStatusShift == 0); return this.flags.status;
const status: int32 = Convert<int32>(this.flags) & kJSPromiseStatusMask;
return Convert<PromiseState>(status);
} }
macro SetStatus(status: constexpr PromiseState): void { macro SetStatus(status: constexpr PromiseState): void {
assert(this.Status() == PromiseState::kPending); assert(this.Status() == PromiseState::kPending);
assert(status != PromiseState::kPending); assert(status != PromiseState::kPending);
const mask: Smi = SmiConstant(status); this.flags.status = status;
this.flags = this.flags | mask;
} }
macro HasHandler(): bool { macro HasHandler(): bool {
return (this.flags & kJSPromiseHasHandlerMask) != 0; return this.flags.has_handler;
} }
macro SetHasHandler(): void { macro SetHasHandler(): void {
this.flags |= kJSPromiseHasHandlerMask; this.flags.has_handler = true;
} }
// Smi 0 terminated list of PromiseReaction objects in case the JSPromise was // Smi 0 terminated list of PromiseReaction objects in case the JSPromise was
// not settled yet, otherwise the result. // not settled yet, otherwise the result.
reactions_or_result: Zero|PromiseReaction|JSAny; reactions_or_result: Zero|PromiseReaction|JSAny;
flags: Smi; flags: SmiTagged<JSPromiseFlags>;
} }

View File

@ -5876,13 +5876,13 @@ class StringSharedKey : public HashTableKey {
}; };
v8::Promise::PromiseState JSPromise::status() const { v8::Promise::PromiseState JSPromise::status() const {
int value = flags() & kStatusMask; int value = flags() & StatusBits::kMask;
DCHECK(value == 0 || value == 1 || value == 2); DCHECK(value == 0 || value == 1 || value == 2);
return static_cast<v8::Promise::PromiseState>(value); return static_cast<v8::Promise::PromiseState>(value);
} }
void JSPromise::set_status(Promise::PromiseState status) { void JSPromise::set_status(Promise::PromiseState status) {
int value = flags() & ~kStatusMask; int value = flags() & ~StatusBits::kMask;
set_flags(value | status); set_flags(value | status);
} }
@ -5900,11 +5900,11 @@ const char* JSPromise::Status(v8::Promise::PromiseState status) {
} }
int JSPromise::async_task_id() const { int JSPromise::async_task_id() const {
return AsyncTaskIdField::decode(flags()); return AsyncTaskIdBits::decode(flags());
} }
void JSPromise::set_async_task_id(int id) { void JSPromise::set_async_task_id(int id) {
set_flags(AsyncTaskIdField::update(flags(), id)); set_flags(AsyncTaskIdBits::update(flags(), id));
} }
// static // static

View File

@ -957,7 +957,8 @@ void CSAGenerator::EmitInstruction(const StoreBitFieldInstruction& instruction,
"CodeStubAssembler(state_)." + encoder + "<" + "CodeStubAssembler(state_)." + encoder + "<" +
GetBitFieldSpecialization(struct_type, instruction.bit_field) + GetBitFieldSpecialization(struct_type, instruction.bit_field) +
">(ca_.UncheckedCast<" + struct_word_type + ">(" + bit_field_struct + ">(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) { if (smi_tagged_type) {
result_expression = result_expression =

View File

@ -1192,30 +1192,6 @@ VisitResult ImplementationVisitor::Visit(StatementExpression* expr) {
return VisitResult{Visit(expr->statement), assembler().TopRange(0)}; return VisitResult{Visit(expr->statement), assembler().TopRange(0)};
} }
void ImplementationVisitor::CheckInitializersWellformed(
const std::string& aggregate_name,
const std::vector<Field>& aggregate_fields,
const std::vector<NameAndExpression>& 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( InitializerResults ImplementationVisitor::VisitInitializerResults(
const ClassType* class_type, const ClassType* class_type,
const std::vector<NameAndExpression>& initializers) { const std::vector<NameAndExpression>& initializers) {
@ -1924,21 +1900,57 @@ VisitResult ImplementationVisitor::Visit(StructExpression* expr) {
} }
// Compute and check struct type from given struct name and argument types // 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); expr->type, term_argument_types);
CheckInitializersWellformed(struct_type->name(), struct_type->fields(), if (const auto* struct_type = StructType::DynamicCast(type)) {
initializers); CheckInitializersWellformed(struct_type->name(), struct_type->fields(),
initializers);
// Implicitly convert values and thereby build the struct on the stack // Implicitly convert values and thereby build the struct on the stack
StackRange struct_range = assembler().TopRange(0); StackRange struct_range = assembler().TopRange(0);
auto& fields = struct_type->fields(); auto& fields = struct_type->fields();
for (size_t i = 0; i < values.size(); i++) { for (size_t i = 0; i < values.size(); i++) {
values[i] = values[i] =
GenerateImplicitConvert(fields[i].name_and_type.type, values[i]); GenerateImplicitConvert(fields[i].name_and_type.type, values[i]);
struct_range.Extend(values[i].stack_range()); 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( LocationReference ImplementationVisitor::GetLocationReference(
@ -2282,13 +2294,11 @@ void ImplementationVisitor::GenerateAssignToLocation(
GenerateFetchFromLocation(reference.bit_field_struct_location()); GenerateFetchFromLocation(reference.bit_field_struct_location());
VisitResult converted_value = VisitResult converted_value =
GenerateImplicitConvert(reference.ReferencedType(), assignment_value); GenerateImplicitConvert(reference.ReferencedType(), assignment_value);
GenerateCopy(bit_field_struct); VisitResult updated_bit_field_struct =
GenerateCopy(converted_value); GenerateSetBitField(bit_field_struct.type(), reference.bit_field(),
assembler().Emit(StoreBitFieldInstruction{bit_field_struct.type(), bit_field_struct, converted_value);
reference.bit_field()}); GenerateAssignToLocation(reference.bit_field_struct_location(),
GenerateAssignToLocation( updated_bit_field_struct);
reference.bit_field_struct_location(),
VisitResult(bit_field_struct.type(), assembler().TopRange(1)));
} else { } else {
DCHECK(reference.IsTemporary()); DCHECK(reference.IsTemporary());
ReportError("cannot assign to const-bound or temporary ", ReportError("cannot assign to const-bound or temporary ",

View File

@ -432,11 +432,29 @@ class ImplementationVisitor {
VisitResult Visit(Expression* expr); VisitResult Visit(Expression* expr);
const Type* Visit(Statement* stmt); const Type* Visit(Statement* stmt);
template <typename T>
void CheckInitializersWellformed( void CheckInitializersWellformed(
const std::string& aggregate_name, const std::string& aggregate_name, const std::vector<T>& aggregate_fields,
const std::vector<Field>& aggregate_fields,
const std::vector<NameAndExpression>& initializers, const std::vector<NameAndExpression>& 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( InitializerResults VisitInitializerResults(
const ClassType* class_type, const ClassType* class_type,
@ -713,6 +731,12 @@ class ImplementationVisitor {
StackRange GenerateLabelGoto(LocalLabel* label, StackRange GenerateLabelGoto(LocalLabel* label,
base::Optional<StackRange> arguments = {}); base::Optional<StackRange> arguments = {});
VisitResult GenerateSetBitField(const Type* bitfield_struct_type,
const BitField& bitfield,
VisitResult bitfield_struct,
VisitResult value,
bool starts_as_zero = false);
std::vector<Binding<LocalLabel>*> LabelsFromIdentifiers( std::vector<Binding<LocalLabel>*> LabelsFromIdentifiers(
const std::vector<Identifier*>& names); const std::vector<Identifier*>& names);

View File

@ -364,14 +364,17 @@ struct LoadBitFieldInstruction : InstructionBase {
struct StoreBitFieldInstruction : InstructionBase { struct StoreBitFieldInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATE() TORQUE_INSTRUCTION_BOILERPLATE()
StoreBitFieldInstruction(const Type* bit_field_struct_type, 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_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; DefinitionLocation GetValueDefinition() const;
const Type* bit_field_struct_type; const Type* bit_field_struct_type;
BitField bit_field; BitField bit_field;
// Allows skipping the mask step if we know the starting value is zero.
bool starts_as_zero;
}; };
struct CallIntrinsicInstruction : InstructionBase { struct CallIntrinsicInstruction : InstructionBase {

View File

@ -452,7 +452,7 @@ void TypeVisitor::VisitStructMethods(
DeclareMethods(struct_type, struct_declaration->methods); DeclareMethods(struct_type, struct_declaration->methods);
} }
const StructType* TypeVisitor::ComputeTypeForStructExpression( const Type* TypeVisitor::ComputeTypeForStructExpression(
TypeExpression* type_expression, TypeExpression* type_expression,
const std::vector<const Type*>& term_argument_types) { const std::vector<const Type*>& term_argument_types) {
auto* basic = BasicTypeExpression::DynamicCast(type_expression); auto* basic = BasicTypeExpression::DynamicCast(type_expression);
@ -472,11 +472,11 @@ const StructType* TypeVisitor::ComputeTypeForStructExpression(
// Compute types of non-generic structs as usual // Compute types of non-generic structs as usual
if (!(maybe_generic_type && decl)) { if (!(maybe_generic_type && decl)) {
const Type* type = ComputeType(type_expression); const Type* type = ComputeType(type_expression);
const StructType* struct_type = StructType::DynamicCast(type); if (!type->IsStructType() && !type->IsBitFieldStructType()) {
if (!struct_type) { ReportError(*type,
ReportError(*type, " is not a struct, but used like one"); " is not a struct or bitfield struct, but used like one");
} }
return struct_type; return type;
} }
auto generic_type = *maybe_generic_type; auto generic_type = *maybe_generic_type;

View File

@ -32,7 +32,9 @@ class TypeVisitor {
static void VisitStructMethods(StructType* struct_type, static void VisitStructMethods(StructType* struct_type,
const StructDeclaration* struct_declaration); const StructDeclaration* struct_declaration);
static Signature MakeSignature(const CallableDeclaration* 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, TypeExpression* type_expression,
const std::vector<const Type*>& term_argument_types); const std::vector<const Type*>& term_argument_types);

View File

@ -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<BoolT> a =
m.UncheckedCast<BoolT>(m.Unsigned(m.SmiToInt32(m.Parameter(0))));
TNode<Uint16T> b =
m.UncheckedCast<Uint16T>(m.Unsigned(m.SmiToInt32(m.Parameter(1))));
TNode<Uint32T> c =
m.UncheckedCast<Uint32T>(m.Unsigned(m.SmiToInt32(m.Parameter(2))));
TNode<BoolT> d =
m.UncheckedCast<BoolT>(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) { TEST(TestBitFieldUintptrOps) {
CcTest::InitializeVM(); CcTest::InitializeVM();
Isolate* isolate(CcTest::i_isolate()); Isolate* isolate(CcTest::i_isolate());

View File

@ -1085,6 +1085,12 @@ namespace test {
TestBitFieldLoad(val, !a, b, c, b == 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 // Some other bitfield structs, to verify getting uintptr values out of word32
// structs and vice versa. // structs and vice versa.
bitfield struct TestBitFieldStruct2 extends uint32 { bitfield struct TestBitFieldStruct2 extends uint32 {