[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 {
|
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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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>
|
||||||
|
@ -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());
|
||||||
|
@ -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);
|
||||||
|
@ -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>;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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 =
|
||||||
|
@ -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 ",
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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());
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user