[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:
parent
c948f08f9b
commit
28a1532643
@ -886,6 +886,9 @@ extern macro SmiUntag(Smi): intptr;
|
||||
macro SmiUntag<T: type>(value: SmiTagged<T>): T {
|
||||
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 TaggedIndexToIntPtr(TaggedIndex): intptr;
|
||||
extern macro IntPtrToTaggedIndex(intptr): TaggedIndex;
|
||||
|
@ -90,6 +90,10 @@ FromConstexpr<LanguageModeSmi, constexpr LanguageMode>(
|
||||
c: constexpr LanguageMode): LanguageModeSmi {
|
||||
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 {
|
||||
return i;
|
||||
|
@ -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;
|
||||
|
@ -7419,25 +7419,39 @@ TNode<UintPtrT> CodeStubAssembler::DecodeWord(SloppyTNode<WordT> word,
|
||||
|
||||
TNode<Word32T> CodeStubAssembler::UpdateWord32(TNode<Word32T> word,
|
||||
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);
|
||||
// Ensure the {value} fits fully in the mask.
|
||||
CSA_ASSERT(this, Uint32LessThanOrEqual(value, Uint32Constant(mask >> shift)));
|
||||
TNode<Word32T> encoded_value = Word32Shl(value, Int32Constant(shift));
|
||||
TNode<Word32T> inverted_mask = Int32Constant(~mask);
|
||||
return Word32Or(Word32And(word, inverted_mask), encoded_value);
|
||||
TNode<Word32T> 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<WordT> CodeStubAssembler::UpdateWord(TNode<WordT> word,
|
||||
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);
|
||||
// Ensure the {value} fits fully in the mask.
|
||||
CSA_ASSERT(this,
|
||||
UintPtrLessThanOrEqual(value, UintPtrConstant(mask >> shift)));
|
||||
TNode<WordT> encoded_value = WordShl(value, static_cast<int>(shift));
|
||||
TNode<IntPtrT> inverted_mask = IntPtrConstant(~static_cast<intptr_t>(mask));
|
||||
return WordOr(WordAnd(word, inverted_mask), encoded_value);
|
||||
TNode<WordT> 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) {
|
||||
|
@ -2827,39 +2827,48 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
|
||||
// Returns a node that contains the updated values of a |BitField|.
|
||||
template <typename BitField>
|
||||
TNode<Word32T> UpdateWord32(TNode<Word32T> word, TNode<Uint32T> value) {
|
||||
return UpdateWord32(word, value, BitField::kShift, BitField::kMask);
|
||||
TNode<Word32T> UpdateWord32(TNode<Word32T> word, TNode<Uint32T> 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 <typename BitField>
|
||||
TNode<WordT> UpdateWord(TNode<WordT> word, TNode<UintPtrT> value) {
|
||||
return UpdateWord(word, value, BitField::kShift, BitField::kMask);
|
||||
TNode<WordT> UpdateWord(TNode<WordT> word, TNode<UintPtrT> 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 <typename BitField>
|
||||
TNode<Word32T> UpdateWordInWord32(TNode<Word32T> word,
|
||||
TNode<UintPtrT> value) {
|
||||
return UncheckedCast<Uint32T>(TruncateIntPtrToInt32(
|
||||
Signed(UpdateWord<BitField>(ChangeUint32ToWord(word), value))));
|
||||
TNode<Word32T> UpdateWordInWord32(TNode<Word32T> word, TNode<UintPtrT> value,
|
||||
bool starts_as_zero = false) {
|
||||
return UncheckedCast<Uint32T>(
|
||||
TruncateIntPtrToInt32(Signed(UpdateWord<BitField>(
|
||||
ChangeUint32ToWord(word), value, starts_as_zero))));
|
||||
}
|
||||
|
||||
// Returns a node that contains the updated values of a |BitField|.
|
||||
template <typename BitField>
|
||||
TNode<WordT> UpdateWord32InWord(TNode<WordT> word, TNode<Uint32T> value) {
|
||||
return UpdateWord<BitField>(word, ChangeUint32ToWord(value));
|
||||
TNode<WordT> UpdateWord32InWord(TNode<WordT> word, TNode<Uint32T> 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
|
||||
// at {shift} and fitting in {mask}.
|
||||
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
|
||||
// at {shift} and fitting in {mask}.
|
||||
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.
|
||||
template <typename T>
|
||||
|
@ -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());
|
||||
|
@ -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<JSPromise, JSObject> {
|
||||
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<int, kHandledHintBit + 1, 22>;
|
||||
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);
|
||||
|
@ -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<int32>(this.flags) & kJSPromiseStatusMask;
|
||||
return Convert<PromiseState>(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<JSPromiseFlags>;
|
||||
}
|
||||
|
@ -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<v8::Promise::PromiseState>(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
|
||||
|
@ -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 =
|
||||
|
@ -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<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(
|
||||
const ClassType* class_type,
|
||||
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
|
||||
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 ",
|
||||
|
@ -432,11 +432,29 @@ class ImplementationVisitor {
|
||||
VisitResult Visit(Expression* expr);
|
||||
const Type* Visit(Statement* stmt);
|
||||
|
||||
template <typename T>
|
||||
void CheckInitializersWellformed(
|
||||
const std::string& aggregate_name,
|
||||
const std::vector<Field>& aggregate_fields,
|
||||
const std::string& aggregate_name, const std::vector<T>& aggregate_fields,
|
||||
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(
|
||||
const ClassType* class_type,
|
||||
@ -713,6 +731,12 @@ class ImplementationVisitor {
|
||||
StackRange GenerateLabelGoto(LocalLabel* label,
|
||||
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(
|
||||
const std::vector<Identifier*>& names);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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<const Type*>& 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;
|
||||
|
@ -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<const Type*>& term_argument_types);
|
||||
|
||||
|
@ -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) {
|
||||
CcTest::InitializeVM();
|
||||
Isolate* isolate(CcTest::i_isolate());
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user