[stringrefs] Implement string.const
Current implementation doesn't cache the result, however. Bug: v8:12868 Change-Id: Idd5eb7bbb49d018fec82a80bffb5288c0b6ee0f8 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3695377 Commit-Queue: Andy Wingo <wingo@igalia.com> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Cr-Commit-Position: refs/heads/main@{#81006}
This commit is contained in:
parent
62159ea316
commit
c842874cb5
@ -42,6 +42,7 @@ extern runtime WasmStringNewWtf8(
|
||||
Context, WasmInstanceObject, Smi, Number, Number): String;
|
||||
extern runtime WasmStringNewWtf16(
|
||||
Context, WasmInstanceObject, Smi, Number, Number): String;
|
||||
extern runtime WasmStringConst(Context, WasmInstanceObject, Smi): String;
|
||||
}
|
||||
|
||||
namespace unsafe {
|
||||
@ -795,4 +796,9 @@ builtin WasmStringNewWtf16(
|
||||
LoadContextFromInstance(instance), instance, SmiFromUint32(memory),
|
||||
WasmUint32ToNumber(offset), WasmUint32ToNumber(size));
|
||||
}
|
||||
builtin WasmStringConst(index: uint32): String {
|
||||
const instance = LoadInstanceFromFrame();
|
||||
tail runtime::WasmStringConst(
|
||||
LoadContextFromInstance(instance), instance, SmiFromUint32(index));
|
||||
}
|
||||
}
|
||||
|
@ -5752,6 +5752,11 @@ Node* WasmGraphBuilder::StringNewWtf16(uint32_t memory, Node* offset,
|
||||
gasm_->Uint32Constant(memory), offset, size);
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::StringConst(uint32_t index) {
|
||||
return gasm_->CallBuiltin(Builtin::kWasmStringConst, Operator::kNoDeopt,
|
||||
gasm_->Uint32Constant(index));
|
||||
}
|
||||
|
||||
// 1 bit V8 Smi tag, 31 bits V8 Smi shift, 1 bit i31ref high-bit truncation.
|
||||
constexpr int kI31To32BitSmiShift = 33;
|
||||
|
||||
|
@ -540,6 +540,7 @@ class WasmGraphBuilder {
|
||||
Node** no_match_control, Node** no_match_effect);
|
||||
Node* StringNewWtf8(uint32_t memory, Node* offset, Node* size);
|
||||
Node* StringNewWtf16(uint32_t memory, Node* offset, Node* size);
|
||||
Node* StringConst(uint32_t index);
|
||||
Node* IsNull(Node* object);
|
||||
Node* TypeGuard(Node* value, wasm::ValueType type);
|
||||
|
||||
|
@ -886,5 +886,32 @@ RUNTIME_FUNCTION(Runtime_WasmStringNewWtf16) {
|
||||
return *result;
|
||||
}
|
||||
|
||||
// Returns the new string if the operation succeeds. Otherwise traps.
|
||||
RUNTIME_FUNCTION(Runtime_WasmStringConst) {
|
||||
ClearThreadInWasmScope flag_scope(isolate);
|
||||
DCHECK_EQ(2, args.length());
|
||||
HandleScope scope(isolate);
|
||||
Handle<WasmInstanceObject> instance = args.at<WasmInstanceObject>(0);
|
||||
static_assert(
|
||||
base::IsInRange(wasm::kV8MaxWasmStringLiterals, 0, Smi::kMaxValue));
|
||||
uint32_t index = args.positive_smi_value_at(1);
|
||||
|
||||
DCHECK_LT(index, instance->module()->stringref_literals.size());
|
||||
|
||||
const wasm::WasmStringRefLiteral& literal =
|
||||
instance->module()->stringref_literals[index];
|
||||
const base::Vector<const uint8_t> module_bytes =
|
||||
instance->module_object().native_module()->wire_bytes();
|
||||
const base::Vector<const uint8_t> string_bytes =
|
||||
module_bytes.SubVector(literal.source.offset(),
|
||||
literal.source.offset() + literal.source.length());
|
||||
// TODO(12868): Override any exception with an uncatchable-by-wasm trap?
|
||||
// TODO(12868): No need to re-validate WTF-8. Also, result should be cached.
|
||||
Handle<String> result;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, result, isolate->factory()->NewStringFromWtf8(string_bytes));
|
||||
return *result;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -613,7 +613,8 @@ namespace internal {
|
||||
F(WasmSyncStackLimit, 0, 1) \
|
||||
F(WasmCreateResumePromise, 2, 1) \
|
||||
F(WasmStringNewWtf8, 4, 1) \
|
||||
F(WasmStringNewWtf16, 4, 1)
|
||||
F(WasmStringNewWtf16, 4, 1) \
|
||||
F(WasmStringConst, 2, 1)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_WASM_TEST(F, I) \
|
||||
F(DeserializeWasmModule, 2, 1) \
|
||||
|
@ -6063,7 +6063,22 @@ class LiftoffCompiler {
|
||||
|
||||
void StringConst(FullDecoder* decoder,
|
||||
const StringConstImmediate<validate>& imm, Value* result) {
|
||||
UNIMPLEMENTED();
|
||||
LiftoffRegList pinned;
|
||||
LiftoffRegister index_reg =
|
||||
pinned.set(__ GetUnusedRegister(kGpReg, pinned));
|
||||
__ LoadConstant(index_reg, WasmValue(static_cast<int32_t>(imm.index)));
|
||||
LiftoffAssembler::VarState index_var(kI32, index_reg, 0);
|
||||
|
||||
CallRuntimeStub(WasmCode::kWasmStringConst,
|
||||
MakeSig::Returns(kRef).Params(kI32),
|
||||
{
|
||||
index_var,
|
||||
},
|
||||
decoder->position());
|
||||
RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
|
||||
|
||||
LiftoffRegister result_reg(kReturnRegister0);
|
||||
__ PushRegister(kRef, result_reg);
|
||||
}
|
||||
|
||||
void StringMeasureUtf8(FullDecoder* decoder, const Value& str,
|
||||
|
@ -966,27 +966,28 @@ struct ControlBase : public PcForErrors<validate> {
|
||||
F(NextInstruction, WasmOpcode) \
|
||||
F(Forward, const Value& from, Value* to)
|
||||
|
||||
#define INTERFACE_CONSTANT_FUNCTIONS(F) \
|
||||
F(I32Const, Value* result, int32_t value) \
|
||||
F(I64Const, Value* result, int64_t value) \
|
||||
F(F32Const, Value* result, float value) \
|
||||
F(F64Const, Value* result, double value) \
|
||||
F(S128Const, Simd128Immediate<validate>& imm, Value* result) \
|
||||
F(BinOp, WasmOpcode opcode, const Value& lhs, const Value& rhs, \
|
||||
Value* result) \
|
||||
F(RefNull, ValueType type, Value* result) \
|
||||
F(RefFunc, uint32_t function_index, Value* result) \
|
||||
F(GlobalGet, Value* result, const GlobalIndexImmediate<validate>& imm) \
|
||||
F(StructNewWithRtt, const StructIndexImmediate<validate>& imm, \
|
||||
const Value& rtt, const Value args[], Value* result) \
|
||||
F(StructNewDefault, const StructIndexImmediate<validate>& imm, \
|
||||
const Value& rtt, Value* result) \
|
||||
F(ArrayInit, const ArrayIndexImmediate<validate>& imm, \
|
||||
const base::Vector<Value>& elements, const Value& rtt, Value* result) \
|
||||
F(ArrayInitFromSegment, const ArrayIndexImmediate<validate>& array_imm, \
|
||||
const IndexImmediate<validate>& data_segment, const Value& offset, \
|
||||
const Value& length, const Value& rtt, Value* result) \
|
||||
F(RttCanon, uint32_t type_index, Value* result) \
|
||||
#define INTERFACE_CONSTANT_FUNCTIONS(F) /* force 80 columns */ \
|
||||
F(I32Const, Value* result, int32_t value) \
|
||||
F(I64Const, Value* result, int64_t value) \
|
||||
F(F32Const, Value* result, float value) \
|
||||
F(F64Const, Value* result, double value) \
|
||||
F(S128Const, Simd128Immediate<validate>& imm, Value* result) \
|
||||
F(BinOp, WasmOpcode opcode, const Value& lhs, const Value& rhs, \
|
||||
Value* result) \
|
||||
F(RefNull, ValueType type, Value* result) \
|
||||
F(RefFunc, uint32_t function_index, Value* result) \
|
||||
F(GlobalGet, Value* result, const GlobalIndexImmediate<validate>& imm) \
|
||||
F(StructNewWithRtt, const StructIndexImmediate<validate>& imm, \
|
||||
const Value& rtt, const Value args[], Value* result) \
|
||||
F(StructNewDefault, const StructIndexImmediate<validate>& imm, \
|
||||
const Value& rtt, Value* result) \
|
||||
F(ArrayInit, const ArrayIndexImmediate<validate>& imm, \
|
||||
const base::Vector<Value>& elements, const Value& rtt, Value* result) \
|
||||
F(ArrayInitFromSegment, const ArrayIndexImmediate<validate>& array_imm, \
|
||||
const IndexImmediate<validate>& data_segment, const Value& offset, \
|
||||
const Value& length, const Value& rtt, Value* result) \
|
||||
F(RttCanon, uint32_t type_index, Value* result) \
|
||||
F(StringConst, const StringConstImmediate<validate>& imm, Value* result) \
|
||||
F(DoReturn, uint32_t drop_values)
|
||||
|
||||
#define INTERFACE_NON_CONSTANT_FUNCTIONS(F) /* force 80 columns */ \
|
||||
@ -1135,7 +1136,6 @@ struct ControlBase : public PcForErrors<validate> {
|
||||
const Value& offset, const Value& size, Value* result) \
|
||||
F(StringNewWtf16, const MemoryIndexImmediate<validate>& imm, \
|
||||
const Value& offset, const Value& size, Value* result) \
|
||||
F(StringConst, const StringConstImmediate<validate>& imm, Value* result) \
|
||||
F(StringMeasureUtf8, const Value& str, Value* result) \
|
||||
F(StringMeasureWtf8, const Value& str, Value* result) \
|
||||
F(StringMeasureWtf16, const Value& str, Value* result) \
|
||||
|
@ -1408,7 +1408,7 @@ class WasmGraphBuildingInterface {
|
||||
|
||||
void StringConst(FullDecoder* decoder,
|
||||
const StringConstImmediate<validate>& imm, Value* result) {
|
||||
UNIMPLEMENTED();
|
||||
result->node = builder_->StringConst(imm.index);
|
||||
}
|
||||
|
||||
void StringMeasureUtf8(FullDecoder* decoder, const Value& str,
|
||||
|
@ -132,6 +132,26 @@ void InitExprInterface::StructNewWithRtt(
|
||||
ValueType::Ref(HeapType(imm.index), kNonNullable));
|
||||
}
|
||||
|
||||
void InitExprInterface::StringConst(FullDecoder* decoder,
|
||||
const StringConstImmediate<validate>& imm,
|
||||
Value* result) {
|
||||
if (!generate_value()) return;
|
||||
static_assert(base::IsInRange(kV8MaxWasmStringLiterals, 0, Smi::kMaxValue));
|
||||
|
||||
DCHECK_LT(imm.index, module_->stringref_literals.size());
|
||||
|
||||
const wasm::WasmStringRefLiteral& literal =
|
||||
module_->stringref_literals[imm.index];
|
||||
const base::Vector<const uint8_t> module_bytes =
|
||||
instance_->module_object().native_module()->wire_bytes();
|
||||
const base::Vector<const uint8_t> string_bytes =
|
||||
module_bytes.SubVector(literal.source.offset(),
|
||||
literal.source.offset() + literal.source.length());
|
||||
Handle<String> string =
|
||||
isolate_->factory()->NewStringFromWtf8(string_bytes).ToHandleChecked();
|
||||
result->runtime_value = WasmValue(string, kWasmStringRef);
|
||||
}
|
||||
|
||||
namespace {
|
||||
WasmValue DefaultValueForType(ValueType type, Isolate* isolate) {
|
||||
switch (type.kind()) {
|
||||
|
@ -124,7 +124,8 @@ struct WasmModule;
|
||||
V(WasmOnStackReplace) \
|
||||
V(WasmSuspend) \
|
||||
V(WasmStringNewWtf8) \
|
||||
V(WasmStringNewWtf16)
|
||||
V(WasmStringNewWtf16) \
|
||||
V(WasmStringConst)
|
||||
|
||||
// Sorted, disjoint and non-overlapping memory regions. A region is of the
|
||||
// form [start, end). So there's no [start, end), [end, other_end),
|
||||
|
@ -47,6 +47,8 @@ ValueType WasmInitExpr::type(const WasmModule* module,
|
||||
return ValueType::Ref(immediate().index, kNonNullable);
|
||||
case kRttCanon:
|
||||
return ValueType::Rtt(immediate().heap_type);
|
||||
case kStringConst:
|
||||
return ValueType::Ref(HeapType::kString, kNonNullable);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ class WasmInitExpr : public ZoneObject {
|
||||
kArrayInit,
|
||||
kArrayInitStatic,
|
||||
kRttCanon,
|
||||
kStringConst,
|
||||
};
|
||||
|
||||
union Immediate {
|
||||
@ -147,6 +148,13 @@ class WasmInitExpr : public ZoneObject {
|
||||
return expr;
|
||||
}
|
||||
|
||||
static WasmInitExpr StringConst(uint32_t index) {
|
||||
WasmInitExpr expr;
|
||||
expr.kind_ = kStringConst;
|
||||
expr.immediate_.index = index;
|
||||
return expr;
|
||||
}
|
||||
|
||||
Immediate immediate() const { return immediate_; }
|
||||
Operator kind() const { return kind_; }
|
||||
const ZoneVector<WasmInitExpr>* operands() const { return operands_; }
|
||||
@ -159,6 +167,7 @@ class WasmInitExpr : public ZoneObject {
|
||||
case kGlobalGet:
|
||||
case kRefFuncConst:
|
||||
case kRttCanon:
|
||||
case kStringConst:
|
||||
return immediate().index == other.immediate().index;
|
||||
case kI32Const:
|
||||
return immediate().i32_const == other.immediate().i32_const;
|
||||
|
@ -577,6 +577,11 @@ void WriteInitializerExpressionWithEnd(ZoneBuffer* buffer,
|
||||
buffer->write_u8(static_cast<uint8_t>(kExprRttCanon));
|
||||
buffer->write_i32v(static_cast<int32_t>(init.immediate().index));
|
||||
break;
|
||||
case WasmInitExpr::kStringConst:
|
||||
buffer->write_u8(kGCPrefix);
|
||||
buffer->write_u8(static_cast<uint8_t>(kExprStringConst));
|
||||
buffer->write_u32v(init.immediate().index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -416,6 +416,11 @@ class InitExprInterface {
|
||||
result->init_expr = WasmInitExpr::RttCanon(type_index);
|
||||
}
|
||||
|
||||
void StringConst(FullDecoder* decoder,
|
||||
const StringConstImmediate<validate>& imm, Value* result) {
|
||||
result->init_expr = WasmInitExpr::StringConst(imm.index);
|
||||
}
|
||||
|
||||
void DoReturn(FullDecoder* decoder, uint32_t /*drop_values*/) {
|
||||
// End decoding on "end".
|
||||
decoder->set_end(decoder->pc() + 1);
|
||||
@ -493,6 +498,9 @@ void AppendInitExpr(std::ostream& os, const WasmInitExpr& expr) {
|
||||
case WasmInitExpr::kRttCanon:
|
||||
os << "RttCanon(" << expr.immediate().index;
|
||||
break;
|
||||
case WasmInitExpr::kStringConst:
|
||||
os << "StringConst(" << expr.immediate().index;
|
||||
break;
|
||||
}
|
||||
|
||||
if (append_operands) {
|
||||
|
@ -7,6 +7,7 @@
|
||||
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
|
||||
let kSig_w_ii = makeSig([kWasmI32, kWasmI32], [kWasmStringRef]);
|
||||
let kSig_w_v = makeSig([], [kWasmStringRef]);
|
||||
|
||||
function encodeWtf8(str) {
|
||||
// String iterator coalesces surrogate pairs.
|
||||
@ -34,18 +35,20 @@ function encodeWtf8(str) {
|
||||
return out;
|
||||
}
|
||||
|
||||
let interestingStrings = ['',
|
||||
'ascii',
|
||||
'latin \xa9 1',
|
||||
'two \ucccc byte',
|
||||
'surrogate \ud800\udc000 pair',
|
||||
'isolated \ud800 leading',
|
||||
'isolated \udc00 trailing'];
|
||||
|
||||
function makeWtf8TestDataSegment() {
|
||||
let data = []
|
||||
let valid = {};
|
||||
let invalid = {};
|
||||
|
||||
for (let str of ['',
|
||||
'ascii',
|
||||
'latin \xa9 1',
|
||||
'two \ucccc byte',
|
||||
'surrogate \ud800\udc000 pair',
|
||||
'isolated \ud800 leading',
|
||||
'isolated \udc00 trailing']) {
|
||||
for (let str of interestingStrings) {
|
||||
let bytes = encodeWtf8(str);
|
||||
valid[str] = { offset: data.length, length: bytes.length };
|
||||
for (let byte of bytes) {
|
||||
@ -73,7 +76,7 @@ function makeWtf8TestDataSegment() {
|
||||
builder.addDataSegment(0, data.data);
|
||||
|
||||
builder.addFunction("string_new_wtf8", kSig_w_ii)
|
||||
.exportAs("string_new_wtf8")
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprLocalGet, 0, kExprLocalGet, 1,
|
||||
kGCPrefix, kExprStringNewWtf8, 0
|
||||
@ -104,13 +107,7 @@ function makeWtf16TestDataSegment() {
|
||||
let data = []
|
||||
let valid = {};
|
||||
|
||||
for (let str of ['',
|
||||
'ascii',
|
||||
'latin \xa9 1',
|
||||
'two \ucccc byte',
|
||||
'surrogate \ud800\udc000 pair',
|
||||
'isolated \ud800 leading',
|
||||
'isolated \udc00 trailing']) {
|
||||
for (let str of interestingStrings) {
|
||||
valid[str] = { offset: data.length, length: str.length };
|
||||
for (let byte of encodeWtf16LE(str)) {
|
||||
data.push(byte);
|
||||
@ -128,7 +125,7 @@ function makeWtf16TestDataSegment() {
|
||||
builder.addDataSegment(0, data.data);
|
||||
|
||||
builder.addFunction("string_new_wtf16", kSig_w_ii)
|
||||
.exportAs("string_new_wtf16")
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprLocalGet, 0, kExprLocalGet, 1,
|
||||
kGCPrefix, kExprStringNewWtf16, 0
|
||||
@ -139,3 +136,25 @@ function makeWtf16TestDataSegment() {
|
||||
assertEquals(str, instance.exports.string_new_wtf16(offset, length));
|
||||
}
|
||||
})();
|
||||
|
||||
(function TestStringConst() {
|
||||
let builder = new WasmModuleBuilder();
|
||||
for (let [index, str] of interestingStrings.entries()) {
|
||||
builder.addLiteralStringRef(encodeWtf8(str));
|
||||
|
||||
builder.addFunction("string_const" + index, kSig_w_v)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kGCPrefix, kExprStringConst, index
|
||||
]);
|
||||
|
||||
builder.addGlobal(kWasmStringRef, false, WasmInitExpr.StringConst(index))
|
||||
.exportAs("global" + index);
|
||||
}
|
||||
|
||||
let instance = builder.instantiate();
|
||||
for (let [index, str] of interestingStrings.entries()) {
|
||||
assertEquals(str, instance.exports["string_const" + index]());
|
||||
assertEquals(str, instance.exports["global" + index].value);
|
||||
}
|
||||
})();
|
||||
|
@ -1108,16 +1108,21 @@ class Binary {
|
||||
break;
|
||||
case kExprRttSub:
|
||||
this.emit_init_expr_recursive(expr.parent);
|
||||
this.emit_u8(kGcPrefix);
|
||||
this.emit_u8(kGCPrefix);
|
||||
this.emit_u8(kExprRttSub);
|
||||
this.emit_u32v(expr.value);
|
||||
break;
|
||||
case kExprRttFreshSub:
|
||||
this.emit_init_expr_recursive(expr.parent);
|
||||
this.emit_u8(kGcPrefix);
|
||||
this.emit_u8(kGCPrefix);
|
||||
this.emit_u8(kExprRttFreshSub);
|
||||
this.emit_u32v(expr.value);
|
||||
break;
|
||||
case kExprStringConst:
|
||||
this.emit_u8(kGCPrefix);
|
||||
this.emit_u8(kExprStringConst);
|
||||
this.emit_u32v(expr.index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1306,6 +1311,9 @@ class WasmInitExpr {
|
||||
static RttFreshSub(type, parent) {
|
||||
return {kind: kExprRttFreshSub, value: type, parent: parent};
|
||||
}
|
||||
static StringConst(index) {
|
||||
return {kind: kExprStringConst, index};
|
||||
}
|
||||
|
||||
static defaultFor(type) {
|
||||
switch (type) {
|
||||
|
Loading…
Reference in New Issue
Block a user