Commit 1e277012 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Avoid unnecessary write barriers and improve code generation.

Avoid write barriers when storing values in the root set, and use
cheaper write barriers for storing maps or tagged pointers. Also
improve the generated code for write barriers, utilizing the out
of line code mechanism that is available to TurboFan backends,
which moves the unlikely case out of the hot path.

R=jarin@chromium.org, mstarzinger@chromium.org

Review URL: https://codereview.chromium.org/1414183006

Cr-Commit-Position: refs/heads/master@{#31914}
parent b9d25d86
...@@ -198,6 +198,48 @@ class OutOfLineLoadInteger final : public OutOfLineCode { ...@@ -198,6 +198,48 @@ class OutOfLineLoadInteger final : public OutOfLineCode {
}; };
class OutOfLineRecordWrite final : public OutOfLineCode {
public:
OutOfLineRecordWrite(CodeGenerator* gen, Register object, Register index,
Register value, Register scratch0, Register scratch1,
RecordWriteMode mode)
: OutOfLineCode(gen),
object_(object),
index_(index),
value_(value),
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode) {}
void Generate() final {
if (mode_ > RecordWriteMode::kValueIsPointer) {
__ JumpIfSmi(value_, exit());
}
if (mode_ > RecordWriteMode::kValueIsMap) {
__ CheckPageFlag(value_, scratch0_,
MemoryChunk::kPointersToHereAreInterestingMask, eq,
exit());
}
SaveFPRegsMode const save_fp_mode =
frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
// TODO(turbofan): Once we get frame elision working, we need to save
// and restore lr properly here if the frame was elided.
RecordWriteStub stub(isolate(), object_, scratch0_, scratch1_,
EMIT_REMEMBERED_SET, save_fp_mode);
__ add(scratch1_, object_, index_);
__ CallStub(&stub);
}
private:
Register const object_;
Register const index_;
Register const value_;
Register const scratch0_;
Register const scratch1_;
RecordWriteMode const mode_;
};
Condition FlagsConditionToCondition(FlagsCondition condition) { Condition FlagsConditionToCondition(FlagsCondition condition) {
switch (condition) { switch (condition) {
case kEqual: case kEqual:
...@@ -443,6 +485,23 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -443,6 +485,23 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ TruncateDoubleToI(i.OutputRegister(), i.InputFloat64Register(0)); __ TruncateDoubleToI(i.OutputRegister(), i.InputFloat64Register(0));
DCHECK_EQ(LeaveCC, i.OutputSBit()); DCHECK_EQ(LeaveCC, i.OutputSBit());
break; break;
case kArchStoreWithWriteBarrier: {
RecordWriteMode mode =
static_cast<RecordWriteMode>(MiscField::decode(instr->opcode()));
Register object = i.InputRegister(0);
Register index = i.InputRegister(1);
Register value = i.InputRegister(2);
Register scratch0 = i.TempRegister(0);
Register scratch1 = i.TempRegister(1);
auto ool = new (zone()) OutOfLineRecordWrite(this, object, index, value,
scratch0, scratch1, mode);
__ str(value, MemOperand(object, index));
__ CheckPageFlag(object, scratch0,
MemoryChunk::kPointersFromHereAreInterestingMask, ne,
ool->entry());
__ bind(ool->exit());
break;
}
case kArmAdd: case kArmAdd:
__ add(i.OutputRegister(), i.InputRegister(0), i.InputOperand2(1), __ add(i.OutputRegister(), i.InputRegister(0), i.InputOperand2(1),
i.OutputSBit()); i.OutputSBit());
...@@ -848,19 +907,6 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -848,19 +907,6 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
DCHECK_EQ(LeaveCC, i.OutputSBit()); DCHECK_EQ(LeaveCC, i.OutputSBit());
break; break;
} }
case kArmStoreWriteBarrier: {
Register object = i.InputRegister(0);
Register index = i.InputRegister(1);
Register value = i.InputRegister(2);
__ add(index, object, index);
__ str(value, MemOperand(index));
SaveFPRegsMode mode =
frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
LinkRegisterStatus lr_status = kLRHasNotBeenSaved;
__ RecordWrite(object, index, value, lr_status, mode);
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
}
case kCheckedLoadInt8: case kCheckedLoadInt8:
ASSEMBLE_CHECKED_LOAD_INTEGER(ldrsb); ASSEMBLE_CHECKED_LOAD_INTEGER(ldrsb);
break; break;
......
...@@ -93,8 +93,7 @@ namespace compiler { ...@@ -93,8 +93,7 @@ namespace compiler {
V(ArmLdr) \ V(ArmLdr) \
V(ArmStr) \ V(ArmStr) \
V(ArmPush) \ V(ArmPush) \
V(ArmPoke) \ V(ArmPoke)
V(ArmStoreWriteBarrier)
// Addressing modes represent the "shape" of inputs to an instruction. // Addressing modes represent the "shape" of inputs to an instruction.
......
...@@ -61,7 +61,6 @@ class ArmOperandGenerator : public OperandGenerator { ...@@ -61,7 +61,6 @@ class ArmOperandGenerator : public OperandGenerator {
case kArmStrb: case kArmStrb:
case kArmLdr: case kArmLdr:
case kArmStr: case kArmStr:
case kArmStoreWriteBarrier:
return value >= -4095 && value <= 4095; return value >= -4095 && value <= 4095;
case kArmLdrh: case kArmLdrh:
...@@ -351,49 +350,70 @@ void InstructionSelector::VisitStore(Node* node) { ...@@ -351,49 +350,70 @@ void InstructionSelector::VisitStore(Node* node) {
Node* value = node->InputAt(2); Node* value = node->InputAt(2);
StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node); StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node);
WriteBarrierKind write_barrier_kind = store_rep.write_barrier_kind();
MachineType rep = RepresentationOf(store_rep.machine_type()); MachineType rep = RepresentationOf(store_rep.machine_type());
if (store_rep.write_barrier_kind() == kFullWriteBarrier) {
DCHECK(rep == kRepTagged);
// TODO(dcarney): refactor RecordWrite function to take temp registers
// and pass them here instead of using fixed regs
// TODO(dcarney): handle immediate indices.
InstructionOperand temps[] = {g.TempRegister(r5), g.TempRegister(r6)};
Emit(kArmStoreWriteBarrier, g.NoOutput(), g.UseFixed(base, r4),
g.UseFixed(index, r5), g.UseFixed(value, r6), arraysize(temps), temps);
return;
}
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind());
ArchOpcode opcode;
switch (rep) {
case kRepFloat32:
opcode = kArmVstrF32;
break;
case kRepFloat64:
opcode = kArmVstrF64;
break;
case kRepBit: // Fall through.
case kRepWord8:
opcode = kArmStrb;
break;
case kRepWord16:
opcode = kArmStrh;
break;
case kRepTagged: // Fall through.
case kRepWord32:
opcode = kArmStr;
break;
default:
UNREACHABLE();
return;
}
if (g.CanBeImmediate(index, opcode)) { if (write_barrier_kind != kNoWriteBarrier) {
Emit(opcode | AddressingModeField::encode(kMode_Offset_RI), g.NoOutput(), DCHECK_EQ(kRepTagged, rep);
g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value)); InstructionOperand inputs[3];
size_t input_count = 0;
inputs[input_count++] = g.UseUniqueRegister(base);
inputs[input_count++] = g.UseUniqueRegister(index);
inputs[input_count++] = (write_barrier_kind == kMapWriteBarrier)
? g.UseRegister(value)
: g.UseUniqueRegister(value);
RecordWriteMode record_write_mode = RecordWriteMode::kValueIsAny;
switch (write_barrier_kind) {
case kNoWriteBarrier:
UNREACHABLE();
break;
case kMapWriteBarrier:
record_write_mode = RecordWriteMode::kValueIsMap;
break;
case kPointerWriteBarrier:
record_write_mode = RecordWriteMode::kValueIsPointer;
break;
case kFullWriteBarrier:
record_write_mode = RecordWriteMode::kValueIsAny;
break;
}
InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
size_t const temp_count = arraysize(temps);
InstructionCode code = kArchStoreWithWriteBarrier;
code |= MiscField::encode(static_cast<int>(record_write_mode));
Emit(code, 0, nullptr, input_count, inputs, temp_count, temps);
} else { } else {
Emit(opcode | AddressingModeField::encode(kMode_Offset_RR), g.NoOutput(), ArchOpcode opcode;
g.UseRegister(base), g.UseRegister(index), g.UseRegister(value)); switch (rep) {
case kRepFloat32:
opcode = kArmVstrF32;
break;
case kRepFloat64:
opcode = kArmVstrF64;
break;
case kRepBit: // Fall through.
case kRepWord8:
opcode = kArmStrb;
break;
case kRepWord16:
opcode = kArmStrh;
break;
case kRepTagged: // Fall through.
case kRepWord32:
opcode = kArmStr;
break;
default:
UNREACHABLE();
return;
}
if (g.CanBeImmediate(index, opcode)) {
Emit(opcode | AddressingModeField::encode(kMode_Offset_RI), g.NoOutput(),
g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value));
} else {
Emit(opcode | AddressingModeField::encode(kMode_Offset_RR), g.NoOutput(),
g.UseRegister(base), g.UseRegister(index), g.UseRegister(value));
}
} }
} }
......
...@@ -255,6 +255,48 @@ class OutOfLineLoadZero final : public OutOfLineCode { ...@@ -255,6 +255,48 @@ class OutOfLineLoadZero final : public OutOfLineCode {
}; };
class OutOfLineRecordWrite final : public OutOfLineCode {
public:
OutOfLineRecordWrite(CodeGenerator* gen, Register object, Register index,
Register value, Register scratch0, Register scratch1,
RecordWriteMode mode)
: OutOfLineCode(gen),
object_(object),
index_(index),
value_(value),
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode) {}
void Generate() final {
if (mode_ > RecordWriteMode::kValueIsPointer) {
__ JumpIfSmi(value_, exit());
}
if (mode_ > RecordWriteMode::kValueIsMap) {
__ CheckPageFlagClear(value_, scratch0_,
MemoryChunk::kPointersToHereAreInterestingMask,
exit());
}
SaveFPRegsMode const save_fp_mode =
frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
// TODO(turbofan): Once we get frame elision working, we need to save
// and restore lr properly here if the frame was elided.
RecordWriteStub stub(isolate(), object_, scratch0_, scratch1_,
EMIT_REMEMBERED_SET, save_fp_mode);
__ Add(scratch1_, object_, index_);
__ CallStub(&stub);
}
private:
Register const object_;
Register const index_;
Register const value_;
Register const scratch0_;
Register const scratch1_;
RecordWriteMode const mode_;
};
Condition FlagsConditionToCondition(FlagsCondition condition) { Condition FlagsConditionToCondition(FlagsCondition condition) {
switch (condition) { switch (condition) {
case kEqual: case kEqual:
...@@ -529,6 +571,23 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -529,6 +571,23 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
case kArchTruncateDoubleToI: case kArchTruncateDoubleToI:
__ TruncateDoubleToI(i.OutputRegister(), i.InputDoubleRegister(0)); __ TruncateDoubleToI(i.OutputRegister(), i.InputDoubleRegister(0));
break; break;
case kArchStoreWithWriteBarrier: {
RecordWriteMode mode =
static_cast<RecordWriteMode>(MiscField::decode(instr->opcode()));
Register object = i.InputRegister(0);
Register index = i.InputRegister(1);
Register value = i.InputRegister(2);
Register scratch0 = i.TempRegister(0);
Register scratch1 = i.TempRegister(1);
auto ool = new (zone()) OutOfLineRecordWrite(this, object, index, value,
scratch0, scratch1, mode);
__ Str(value, MemOperand(object, index));
__ CheckPageFlagSet(object, scratch0,
MemoryChunk::kPointersFromHereAreInterestingMask,
ool->entry());
__ Bind(ool->exit());
break;
}
case kArm64Float64RoundDown: case kArm64Float64RoundDown:
__ Frintm(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); __ Frintm(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
break; break;
...@@ -995,29 +1054,6 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -995,29 +1054,6 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
case kArm64StrD: case kArm64StrD:
__ Str(i.InputDoubleRegister(2), i.MemoryOperand()); __ Str(i.InputDoubleRegister(2), i.MemoryOperand());
break; break;
case kArm64StoreWriteBarrier: {
Register object = i.InputRegister(0);
Register index = i.InputRegister(1);
Register value = i.InputRegister(2);
__ Add(index, object, index);
__ Str(value, MemOperand(index));
SaveFPRegsMode mode =
frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
// TODO(dcarney): we shouldn't test write barriers from c calls.
LinkRegisterStatus lr_status = kLRHasNotBeenSaved;
UseScratchRegisterScope scope(masm());
Register temp = no_reg;
if (csp.is(masm()->StackPointer())) {
temp = scope.AcquireX();
lr_status = kLRHasBeenSaved;
__ Push(lr, temp); // Need to push a pair
}
__ RecordWrite(object, index, value, lr_status, mode);
if (csp.is(masm()->StackPointer())) {
__ Pop(temp, lr);
}
break;
}
case kCheckedLoadInt8: case kCheckedLoadInt8:
ASSEMBLE_CHECKED_LOAD_INTEGER(Ldrsb); ASSEMBLE_CHECKED_LOAD_INTEGER(Ldrsb);
break; break;
......
...@@ -129,8 +129,7 @@ namespace compiler { ...@@ -129,8 +129,7 @@ namespace compiler {
V(Arm64LdrW) \ V(Arm64LdrW) \
V(Arm64StrW) \ V(Arm64StrW) \
V(Arm64Ldr) \ V(Arm64Ldr) \
V(Arm64Str) \ V(Arm64Str)
V(Arm64StoreWriteBarrier)
// Addressing modes represent the "shape" of inputs to an instruction. // Addressing modes represent the "shape" of inputs to an instruction.
......
...@@ -393,58 +393,80 @@ void InstructionSelector::VisitStore(Node* node) { ...@@ -393,58 +393,80 @@ void InstructionSelector::VisitStore(Node* node) {
Node* value = node->InputAt(2); Node* value = node->InputAt(2);
StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node); StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node);
WriteBarrierKind write_barrier_kind = store_rep.write_barrier_kind();
MachineType rep = RepresentationOf(store_rep.machine_type()); MachineType rep = RepresentationOf(store_rep.machine_type());
if (store_rep.write_barrier_kind() == kFullWriteBarrier) {
DCHECK(rep == kRepTagged); // TODO(arm64): I guess this could be done in a better way.
// TODO(dcarney): refactor RecordWrite function to take temp registers if (write_barrier_kind != kNoWriteBarrier) {
// and pass them here instead of using fixed regs DCHECK_EQ(kRepTagged, rep);
// TODO(dcarney): handle immediate indices. InstructionOperand inputs[3];
InstructionOperand temps[] = {g.TempRegister(x11), g.TempRegister(x12)}; size_t input_count = 0;
Emit(kArm64StoreWriteBarrier, g.NoOutput(), g.UseFixed(base, x10), inputs[input_count++] = g.UseUniqueRegister(base);
g.UseFixed(index, x11), g.UseFixed(value, x12), arraysize(temps), inputs[input_count++] = g.UseUniqueRegister(index);
temps); inputs[input_count++] = (write_barrier_kind == kMapWriteBarrier)
return; ? g.UseRegister(value)
} : g.UseUniqueRegister(value);
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind()); RecordWriteMode record_write_mode = RecordWriteMode::kValueIsAny;
ArchOpcode opcode; switch (write_barrier_kind) {
ImmediateMode immediate_mode = kNoImmediate; case kNoWriteBarrier:
switch (rep) { UNREACHABLE();
case kRepFloat32: break;
opcode = kArm64StrS; case kMapWriteBarrier:
immediate_mode = kLoadStoreImm32; record_write_mode = RecordWriteMode::kValueIsMap;
break; break;
case kRepFloat64: case kPointerWriteBarrier:
opcode = kArm64StrD; record_write_mode = RecordWriteMode::kValueIsPointer;
immediate_mode = kLoadStoreImm64; break;
break; case kFullWriteBarrier:
case kRepBit: // Fall through. record_write_mode = RecordWriteMode::kValueIsAny;
case kRepWord8: break;
opcode = kArm64Strb; }
immediate_mode = kLoadStoreImm8; InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
break; size_t const temp_count = arraysize(temps);
case kRepWord16: InstructionCode code = kArchStoreWithWriteBarrier;
opcode = kArm64Strh; code |= MiscField::encode(static_cast<int>(record_write_mode));
immediate_mode = kLoadStoreImm16; Emit(code, 0, nullptr, input_count, inputs, temp_count, temps);
break;
case kRepWord32:
opcode = kArm64StrW;
immediate_mode = kLoadStoreImm32;
break;
case kRepTagged: // Fall through.
case kRepWord64:
opcode = kArm64Str;
immediate_mode = kLoadStoreImm64;
break;
default:
UNREACHABLE();
return;
}
if (g.CanBeImmediate(index, immediate_mode)) {
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value));
} else { } else {
Emit(opcode | AddressingModeField::encode(kMode_MRR), g.NoOutput(), ArchOpcode opcode;
g.UseRegister(base), g.UseRegister(index), g.UseRegister(value)); ImmediateMode immediate_mode = kNoImmediate;
switch (rep) {
case kRepFloat32:
opcode = kArm64StrS;
immediate_mode = kLoadStoreImm32;
break;
case kRepFloat64:
opcode = kArm64StrD;
immediate_mode = kLoadStoreImm64;
break;
case kRepBit: // Fall through.
case kRepWord8:
opcode = kArm64Strb;
immediate_mode = kLoadStoreImm8;
break;
case kRepWord16:
opcode = kArm64Strh;
immediate_mode = kLoadStoreImm16;
break;
case kRepWord32:
opcode = kArm64StrW;
immediate_mode = kLoadStoreImm32;
break;
case kRepTagged: // Fall through.
case kRepWord64:
opcode = kArm64Str;
immediate_mode = kLoadStoreImm64;
break;
default:
UNREACHABLE();
return;
}
if (g.CanBeImmediate(index, immediate_mode)) {
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value));
} else {
Emit(opcode | AddressingModeField::encode(kMode_MRR), g.NoOutput(),
g.UseRegister(base), g.UseRegister(index), g.UseRegister(value));
}
} }
} }
......
...@@ -147,12 +147,15 @@ class OutOfLineCode : public ZoneObject { ...@@ -147,12 +147,15 @@ class OutOfLineCode : public ZoneObject {
Label* entry() { return &entry_; } Label* entry() { return &entry_; }
Label* exit() { return &exit_; } Label* exit() { return &exit_; }
Frame* frame() const { return frame_; }
Isolate* isolate() const { return masm()->isolate(); }
MacroAssembler* masm() const { return masm_; } MacroAssembler* masm() const { return masm_; }
OutOfLineCode* next() const { return next_; } OutOfLineCode* next() const { return next_; }
private: private:
Label entry_; Label entry_;
Label exit_; Label exit_;
Frame* const frame_;
MacroAssembler* const masm_; MacroAssembler* const masm_;
OutOfLineCode* const next_; OutOfLineCode* const next_;
}; };
......
...@@ -664,7 +664,7 @@ void CodeGenerator::MarkLazyDeoptSite() { ...@@ -664,7 +664,7 @@ void CodeGenerator::MarkLazyDeoptSite() {
OutOfLineCode::OutOfLineCode(CodeGenerator* gen) OutOfLineCode::OutOfLineCode(CodeGenerator* gen)
: masm_(gen->masm()), next_(gen->ools_) { : frame_(gen->frame()), masm_(gen->masm()), next_(gen->ools_) {
gen->ools_ = this; gen->ools_ = this;
} }
......
...@@ -217,6 +217,46 @@ class OutOfLineTruncateDoubleToI final : public OutOfLineCode { ...@@ -217,6 +217,46 @@ class OutOfLineTruncateDoubleToI final : public OutOfLineCode {
XMMRegister const input_; XMMRegister const input_;
}; };
class OutOfLineRecordWrite final : public OutOfLineCode {
public:
OutOfLineRecordWrite(CodeGenerator* gen, Register object, Operand operand,
Register value, Register scratch0, Register scratch1,
RecordWriteMode mode)
: OutOfLineCode(gen),
object_(object),
operand_(operand),
value_(value),
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode) {}
void Generate() final {
if (mode_ > RecordWriteMode::kValueIsPointer) {
__ JumpIfSmi(value_, exit());
}
if (mode_ > RecordWriteMode::kValueIsMap) {
__ CheckPageFlag(value_, scratch0_,
MemoryChunk::kPointersToHereAreInterestingMask, zero,
exit());
}
SaveFPRegsMode const save_fp_mode =
frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
RecordWriteStub stub(isolate(), object_, scratch0_, scratch1_,
EMIT_REMEMBERED_SET, save_fp_mode);
__ lea(scratch1_, operand_);
__ CallStub(&stub);
}
private:
Register const object_;
Operand const operand_;
Register const value_;
Register const scratch0_;
Register const scratch1_;
RecordWriteMode const mode_;
};
} // namespace } // namespace
...@@ -407,6 +447,24 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -407,6 +447,24 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ bind(ool->exit()); __ bind(ool->exit());
break; break;
} }
case kArchStoreWithWriteBarrier: {
RecordWriteMode mode =
static_cast<RecordWriteMode>(MiscField::decode(instr->opcode()));
Register object = i.InputRegister(0);
size_t index = 0;
Operand operand = i.MemoryOperand(&index);
Register value = i.InputRegister(index);
Register scratch0 = i.TempRegister(0);
Register scratch1 = i.TempRegister(1);
auto ool = new (zone()) OutOfLineRecordWrite(this, object, operand, value,
scratch0, scratch1, mode);
__ mov(operand, value);
__ CheckPageFlag(object, scratch0,
MemoryChunk::kPointersFromHereAreInterestingMask,
not_zero, ool->entry());
__ bind(ool->exit());
break;
}
case kIA32Add: case kIA32Add:
if (HasImmediateInput(instr, 1)) { if (HasImmediateInput(instr, 1)) {
__ add(i.InputOperand(0), i.InputImmediate(1)); __ add(i.InputOperand(0), i.InputImmediate(1));
...@@ -920,24 +978,6 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -920,24 +978,6 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
} }
break; break;
} }
case kIA32StoreWriteBarrier: {
Register object = i.InputRegister(0);
Register value = i.InputRegister(2);
SaveFPRegsMode mode =
frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
if (HasImmediateInput(instr, 1)) {
int index = i.InputInt32(1);
Register scratch = i.TempRegister(1);
__ mov(Operand(object, index), value);
__ RecordWriteContextSlot(object, index, value, scratch, mode);
} else {
Register index = i.InputRegister(1);
__ mov(Operand(object, index, times_1, 0), value);
__ lea(index, Operand(object, index, times_1, 0));
__ RecordWrite(object, index, value, mode);
}
break;
}
case kCheckedLoadInt8: case kCheckedLoadInt8:
ASSEMBLE_CHECKED_LOAD_INTEGER(movsx_b); ASSEMBLE_CHECKED_LOAD_INTEGER(movsx_b);
break; break;
......
...@@ -96,7 +96,6 @@ namespace compiler { ...@@ -96,7 +96,6 @@ namespace compiler {
V(IA32Lea) \ V(IA32Lea) \
V(IA32Push) \ V(IA32Push) \
V(IA32Poke) \ V(IA32Poke) \
V(IA32StoreWriteBarrier) \
V(IA32StackCheck) V(IA32StackCheck)
......
...@@ -215,66 +215,89 @@ void InstructionSelector::VisitStore(Node* node) { ...@@ -215,66 +215,89 @@ void InstructionSelector::VisitStore(Node* node) {
Node* value = node->InputAt(2); Node* value = node->InputAt(2);
StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node); StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node);
WriteBarrierKind write_barrier_kind = store_rep.write_barrier_kind();
MachineType rep = RepresentationOf(store_rep.machine_type()); MachineType rep = RepresentationOf(store_rep.machine_type());
if (store_rep.write_barrier_kind() == kFullWriteBarrier) {
if (write_barrier_kind != kNoWriteBarrier) {
DCHECK_EQ(kRepTagged, rep); DCHECK_EQ(kRepTagged, rep);
// TODO(dcarney): refactor RecordWrite function to take temp registers AddressingMode addressing_mode;
// and pass them here instead of using fixed regs InstructionOperand inputs[3];
size_t input_count = 0;
inputs[input_count++] = g.UseUniqueRegister(base);
if (g.CanBeImmediate(index)) { if (g.CanBeImmediate(index)) {
InstructionOperand temps[] = {g.TempRegister(ecx), g.TempRegister()}; inputs[input_count++] = g.UseImmediate(index);
Emit(kIA32StoreWriteBarrier, g.NoOutput(), g.UseFixed(base, ebx), addressing_mode = kMode_MRI;
g.UseImmediate(index), g.UseFixed(value, ecx), arraysize(temps),
temps);
} else { } else {
InstructionOperand temps[] = {g.TempRegister(ecx), g.TempRegister(edx)}; inputs[input_count++] = g.UseUniqueRegister(index);
Emit(kIA32StoreWriteBarrier, g.NoOutput(), g.UseFixed(base, ebx), addressing_mode = kMode_MR1;
g.UseFixed(index, ecx), g.UseFixed(value, edx), arraysize(temps), }
temps); inputs[input_count++] = (write_barrier_kind == kMapWriteBarrier)
? g.UseRegister(value)
: g.UseUniqueRegister(value);
RecordWriteMode record_write_mode = RecordWriteMode::kValueIsAny;
switch (write_barrier_kind) {
case kNoWriteBarrier:
UNREACHABLE();
break;
case kMapWriteBarrier:
record_write_mode = RecordWriteMode::kValueIsMap;
break;
case kPointerWriteBarrier:
record_write_mode = RecordWriteMode::kValueIsPointer;
break;
case kFullWriteBarrier:
record_write_mode = RecordWriteMode::kValueIsAny;
break;
}
InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
size_t const temp_count = arraysize(temps);
InstructionCode code = kArchStoreWithWriteBarrier;
code |= AddressingModeField::encode(addressing_mode);
code |= MiscField::encode(static_cast<int>(record_write_mode));
Emit(code, 0, nullptr, input_count, inputs, temp_count, temps);
} else {
ArchOpcode opcode;
switch (rep) {
case kRepFloat32:
opcode = kIA32Movss;
break;
case kRepFloat64:
opcode = kIA32Movsd;
break;
case kRepBit: // Fall through.
case kRepWord8:
opcode = kIA32Movb;
break;
case kRepWord16:
opcode = kIA32Movw;
break;
case kRepTagged: // Fall through.
case kRepWord32:
opcode = kIA32Movl;
break;
default:
UNREACHABLE();
return;
} }
return;
}
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind());
ArchOpcode opcode; InstructionOperand val;
switch (rep) { if (g.CanBeImmediate(value)) {
case kRepFloat32: val = g.UseImmediate(value);
opcode = kIA32Movss; } else if (rep == kRepWord8 || rep == kRepBit) {
break; val = g.UseByteRegister(value);
case kRepFloat64: } else {
opcode = kIA32Movsd; val = g.UseRegister(value);
break; }
case kRepBit: // Fall through.
case kRepWord8:
opcode = kIA32Movb;
break;
case kRepWord16:
opcode = kIA32Movw;
break;
case kRepTagged: // Fall through.
case kRepWord32:
opcode = kIA32Movl;
break;
default:
UNREACHABLE();
return;
}
InstructionOperand val; InstructionOperand inputs[4];
if (g.CanBeImmediate(value)) { size_t input_count = 0;
val = g.UseImmediate(value); AddressingMode addressing_mode =
} else if (rep == kRepWord8 || rep == kRepBit) { g.GetEffectiveAddressMemoryOperand(node, inputs, &input_count);
val = g.UseByteRegister(value); InstructionCode code =
} else { opcode | AddressingModeField::encode(addressing_mode);
val = g.UseRegister(value); inputs[input_count++] = val;
Emit(code, 0, static_cast<InstructionOperand*>(NULL), input_count, inputs);
} }
InstructionOperand inputs[4];
size_t input_count = 0;
AddressingMode mode =
g.GetEffectiveAddressMemoryOperand(node, inputs, &input_count);
InstructionCode code = opcode | AddressingModeField::encode(mode);
inputs[input_count++] = val;
Emit(code, 0, static_cast<InstructionOperand*>(NULL), input_count, inputs);
} }
......
...@@ -33,39 +33,44 @@ namespace v8 { ...@@ -33,39 +33,44 @@ namespace v8 {
namespace internal { namespace internal {
namespace compiler { namespace compiler {
// Modes for ArchStoreWithWriteBarrier below.
enum class RecordWriteMode { kValueIsMap, kValueIsPointer, kValueIsAny };
// Target-specific opcodes that specify which assembly sequence to emit. // Target-specific opcodes that specify which assembly sequence to emit.
// Most opcodes specify a single instruction. // Most opcodes specify a single instruction.
#define ARCH_OPCODE_LIST(V) \ #define ARCH_OPCODE_LIST(V) \
V(ArchCallCodeObject) \ V(ArchCallCodeObject) \
V(ArchTailCallCodeObject) \ V(ArchTailCallCodeObject) \
V(ArchCallJSFunction) \ V(ArchCallJSFunction) \
V(ArchTailCallJSFunction) \ V(ArchTailCallJSFunction) \
V(ArchPrepareCallCFunction) \ V(ArchPrepareCallCFunction) \
V(ArchCallCFunction) \ V(ArchCallCFunction) \
V(ArchLazyBailout) \ V(ArchLazyBailout) \
V(ArchJmp) \ V(ArchJmp) \
V(ArchLookupSwitch) \ V(ArchLookupSwitch) \
V(ArchTableSwitch) \ V(ArchTableSwitch) \
V(ArchNop) \ V(ArchNop) \
V(ArchDeoptimize) \ V(ArchDeoptimize) \
V(ArchRet) \ V(ArchRet) \
V(ArchStackPointer) \ V(ArchStackPointer) \
V(ArchFramePointer) \ V(ArchFramePointer) \
V(ArchTruncateDoubleToI) \ V(ArchTruncateDoubleToI) \
V(CheckedLoadInt8) \ V(ArchStoreWithWriteBarrier) \
V(CheckedLoadUint8) \ V(CheckedLoadInt8) \
V(CheckedLoadInt16) \ V(CheckedLoadUint8) \
V(CheckedLoadUint16) \ V(CheckedLoadInt16) \
V(CheckedLoadWord32) \ V(CheckedLoadUint16) \
V(CheckedLoadWord64) \ V(CheckedLoadWord32) \
V(CheckedLoadFloat32) \ V(CheckedLoadWord64) \
V(CheckedLoadFloat64) \ V(CheckedLoadFloat32) \
V(CheckedStoreWord8) \ V(CheckedLoadFloat64) \
V(CheckedStoreWord16) \ V(CheckedStoreWord8) \
V(CheckedStoreWord32) \ V(CheckedStoreWord16) \
V(CheckedStoreWord64) \ V(CheckedStoreWord32) \
V(CheckedStoreFloat32) \ V(CheckedStoreWord64) \
V(CheckedStoreFloat64) \ V(CheckedStoreFloat32) \
V(CheckedStoreFloat64) \
TARGET_ARCH_OPCODE_LIST(V) TARGET_ARCH_OPCODE_LIST(V)
enum ArchOpcode { enum ArchOpcode {
......
...@@ -34,6 +34,10 @@ std::ostream& operator<<(std::ostream& os, WriteBarrierKind kind) { ...@@ -34,6 +34,10 @@ std::ostream& operator<<(std::ostream& os, WriteBarrierKind kind) {
switch (kind) { switch (kind) {
case kNoWriteBarrier: case kNoWriteBarrier:
return os << "NoWriteBarrier"; return os << "NoWriteBarrier";
case kMapWriteBarrier:
return os << "MapWriteBarrier";
case kPointerWriteBarrier:
return os << "PointerWriteBarrier";
case kFullWriteBarrier: case kFullWriteBarrier:
return os << "FullWriteBarrier"; return os << "FullWriteBarrier";
} }
...@@ -255,6 +259,16 @@ struct MachineOperatorGlobalCache { ...@@ -255,6 +259,16 @@ struct MachineOperatorGlobalCache {
Store##Type##NoWriteBarrier##Operator() \ Store##Type##NoWriteBarrier##Operator() \
: Store##Type##Operator(kNoWriteBarrier) {} \ : Store##Type##Operator(kNoWriteBarrier) {} \
}; \ }; \
struct Store##Type##MapWriteBarrier##Operator final \
: public Store##Type##Operator { \
Store##Type##MapWriteBarrier##Operator() \
: Store##Type##Operator(kMapWriteBarrier) {} \
}; \
struct Store##Type##PointerWriteBarrier##Operator final \
: public Store##Type##Operator { \
Store##Type##PointerWriteBarrier##Operator() \
: Store##Type##Operator(kPointerWriteBarrier) {} \
}; \
struct Store##Type##FullWriteBarrier##Operator final \ struct Store##Type##FullWriteBarrier##Operator final \
: public Store##Type##Operator { \ : public Store##Type##Operator { \
Store##Type##FullWriteBarrier##Operator() \ Store##Type##FullWriteBarrier##Operator() \
...@@ -268,6 +282,9 @@ struct MachineOperatorGlobalCache { ...@@ -268,6 +282,9 @@ struct MachineOperatorGlobalCache {
"CheckedStore", 4, 1, 1, 0, 1, 0, k##Type) {} \ "CheckedStore", 4, 1, 1, 0, 1, 0, k##Type) {} \
}; \ }; \
Store##Type##NoWriteBarrier##Operator kStore##Type##NoWriteBarrier; \ Store##Type##NoWriteBarrier##Operator kStore##Type##NoWriteBarrier; \
Store##Type##MapWriteBarrier##Operator kStore##Type##MapWriteBarrier; \
Store##Type##PointerWriteBarrier##Operator \
kStore##Type##PointerWriteBarrier; \
Store##Type##FullWriteBarrier##Operator kStore##Type##FullWriteBarrier; \ Store##Type##FullWriteBarrier##Operator kStore##Type##FullWriteBarrier; \
CheckedStore##Type##Operator kCheckedStore##Type; CheckedStore##Type##Operator kCheckedStore##Type;
MACHINE_TYPE_LIST(STORE) MACHINE_TYPE_LIST(STORE)
...@@ -331,14 +348,18 @@ const Operator* MachineOperatorBuilder::Load(LoadRepresentation rep) { ...@@ -331,14 +348,18 @@ const Operator* MachineOperatorBuilder::Load(LoadRepresentation rep) {
const Operator* MachineOperatorBuilder::Store(StoreRepresentation rep) { const Operator* MachineOperatorBuilder::Store(StoreRepresentation rep) {
switch (rep.machine_type()) { switch (rep.machine_type()) {
#define STORE(Type) \ #define STORE(Type) \
case k##Type: \ case k##Type: \
switch (rep.write_barrier_kind()) { \ switch (rep.write_barrier_kind()) { \
case kNoWriteBarrier: \ case kNoWriteBarrier: \
return &cache_.k##Store##Type##NoWriteBarrier; \ return &cache_.k##Store##Type##NoWriteBarrier; \
case kFullWriteBarrier: \ case kMapWriteBarrier: \
return &cache_.k##Store##Type##FullWriteBarrier; \ return &cache_.k##Store##Type##MapWriteBarrier; \
} \ case kPointerWriteBarrier: \
return &cache_.k##Store##Type##PointerWriteBarrier; \
case kFullWriteBarrier: \
return &cache_.k##Store##Type##FullWriteBarrier; \
} \
break; break;
MACHINE_TYPE_LIST(STORE) MACHINE_TYPE_LIST(STORE)
#undef STORE #undef STORE
......
...@@ -49,7 +49,12 @@ TruncationMode TruncationModeOf(Operator const*); ...@@ -49,7 +49,12 @@ TruncationMode TruncationModeOf(Operator const*);
// Supported write barrier modes. // Supported write barrier modes.
enum WriteBarrierKind { kNoWriteBarrier, kFullWriteBarrier }; enum WriteBarrierKind {
kNoWriteBarrier,
kMapWriteBarrier,
kPointerWriteBarrier,
kFullWriteBarrier
};
std::ostream& operator<<(std::ostream& os, WriteBarrierKind); std::ostream& operator<<(std::ostream& os, WriteBarrierKind);
......
...@@ -213,6 +213,48 @@ class OutOfLineCeil final : public OutOfLineRound { ...@@ -213,6 +213,48 @@ class OutOfLineCeil final : public OutOfLineRound {
}; };
class OutOfLineRecordWrite final : public OutOfLineCode {
public:
OutOfLineRecordWrite(CodeGenerator* gen, Register object, Register index,
Register value, Register scratch0, Register scratch1,
RecordWriteMode mode)
: OutOfLineCode(gen),
object_(object),
index_(index),
value_(value),
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode) {}
void Generate() final {
if (mode_ > RecordWriteMode::kValueIsPointer) {
__ JumpIfSmi(value_, exit());
}
if (mode_ > RecordWriteMode::kValueIsMap) {
__ CheckPageFlag(value_, scratch0_,
MemoryChunk::kPointersToHereAreInterestingMask, eq,
exit());
}
SaveFPRegsMode const save_fp_mode =
frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
// TODO(turbofan): Once we get frame elision working, we need to save
// and restore lr properly here if the frame was elided.
RecordWriteStub stub(isolate(), object_, scratch0_, scratch1_,
EMIT_REMEMBERED_SET, save_fp_mode);
__ Addu(scratch1_, object_, index_);
__ CallStub(&stub);
}
private:
Register const object_;
Register const index_;
Register const value_;
Register const scratch0_;
Register const scratch1_;
RecordWriteMode const mode_;
};
Condition FlagsConditionToConditionCmp(FlagsCondition condition) { Condition FlagsConditionToConditionCmp(FlagsCondition condition) {
switch (condition) { switch (condition) {
case kEqual: case kEqual:
...@@ -520,6 +562,24 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -520,6 +562,24 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
case kArchTruncateDoubleToI: case kArchTruncateDoubleToI:
__ TruncateDoubleToI(i.OutputRegister(), i.InputDoubleRegister(0)); __ TruncateDoubleToI(i.OutputRegister(), i.InputDoubleRegister(0));
break; break;
case kArchStoreWithWriteBarrier: {
RecordWriteMode mode =
static_cast<RecordWriteMode>(MiscField::decode(instr->opcode()));
Register object = i.InputRegister(0);
Register index = i.InputRegister(1);
Register value = i.InputRegister(2);
Register scratch0 = i.TempRegister(0);
Register scratch1 = i.TempRegister(1);
auto ool = new (zone()) OutOfLineRecordWrite(this, object, index, value,
scratch0, scratch1, mode);
__ Addu(at, object, index);
__ sw(value, MemOperand(at));
__ CheckPageFlag(object, scratch0,
MemoryChunk::kPointersFromHereAreInterestingMask, ne,
ool->entry());
__ bind(ool->exit());
break;
}
case kMipsAdd: case kMipsAdd:
__ Addu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); __ Addu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break; break;
...@@ -883,18 +943,6 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -883,18 +943,6 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
} }
break; break;
} }
case kMipsStoreWriteBarrier: {
Register object = i.InputRegister(0);
Register index = i.InputRegister(1);
Register value = i.InputRegister(2);
__ addu(index, object, index);
__ sw(value, MemOperand(index));
SaveFPRegsMode mode =
frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
RAStatus ra_status = kRAHasNotBeenSaved;
__ RecordWrite(object, index, value, ra_status, mode);
break;
}
case kCheckedLoadInt8: case kCheckedLoadInt8:
ASSEMBLE_CHECKED_LOAD_INTEGER(lb); ASSEMBLE_CHECKED_LOAD_INTEGER(lb);
break; break;
......
...@@ -85,8 +85,7 @@ namespace compiler { ...@@ -85,8 +85,7 @@ namespace compiler {
V(MipsFloat32Min) \ V(MipsFloat32Min) \
V(MipsPush) \ V(MipsPush) \
V(MipsStoreToStackSlot) \ V(MipsStoreToStackSlot) \
V(MipsStackClaim) \ V(MipsStackClaim)
V(MipsStoreWriteBarrier)
// Addressing modes represent the "shape" of inputs to an instruction. // Addressing modes represent the "shape" of inputs to an instruction.
......
...@@ -178,53 +178,75 @@ void InstructionSelector::VisitStore(Node* node) { ...@@ -178,53 +178,75 @@ void InstructionSelector::VisitStore(Node* node) {
Node* value = node->InputAt(2); Node* value = node->InputAt(2);
StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node); StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node);
WriteBarrierKind write_barrier_kind = store_rep.write_barrier_kind();
MachineType rep = RepresentationOf(store_rep.machine_type()); MachineType rep = RepresentationOf(store_rep.machine_type());
if (store_rep.write_barrier_kind() == kFullWriteBarrier) {
DCHECK(rep == kRepTagged);
// TODO(dcarney): refactor RecordWrite function to take temp registers
// and pass them here instead of using fixed regs
// TODO(dcarney): handle immediate indices.
InstructionOperand temps[] = {g.TempRegister(t1), g.TempRegister(t2)};
Emit(kMipsStoreWriteBarrier, g.NoOutput(), g.UseFixed(base, t0),
g.UseFixed(index, t1), g.UseFixed(value, t2), arraysize(temps), temps);
return;
}
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind());
ArchOpcode opcode;
switch (rep) {
case kRepFloat32:
opcode = kMipsSwc1;
break;
case kRepFloat64:
opcode = kMipsSdc1;
break;
case kRepBit: // Fall through.
case kRepWord8:
opcode = kMipsSb;
break;
case kRepWord16:
opcode = kMipsSh;
break;
case kRepTagged: // Fall through.
case kRepWord32:
opcode = kMipsSw;
break;
default:
UNREACHABLE();
return;
}
if (g.CanBeImmediate(index, opcode)) { // TODO(mips): I guess this could be done in a better way.
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), if (write_barrier_kind != kNoWriteBarrier) {
g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value)); DCHECK_EQ(kRepTagged, rep);
InstructionOperand inputs[3];
size_t input_count = 0;
inputs[input_count++] = g.UseUniqueRegister(base);
inputs[input_count++] = g.UseUniqueRegister(index);
inputs[input_count++] = (write_barrier_kind == kMapWriteBarrier)
? g.UseRegister(value)
: g.UseUniqueRegister(value);
RecordWriteMode record_write_mode = RecordWriteMode::kValueIsAny;
switch (write_barrier_kind) {
case kNoWriteBarrier:
UNREACHABLE();
break;
case kMapWriteBarrier:
record_write_mode = RecordWriteMode::kValueIsMap;
break;
case kPointerWriteBarrier:
record_write_mode = RecordWriteMode::kValueIsPointer;
break;
case kFullWriteBarrier:
record_write_mode = RecordWriteMode::kValueIsAny;
break;
}
InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
size_t const temp_count = arraysize(temps);
InstructionCode code = kArchStoreWithWriteBarrier;
code |= MiscField::encode(static_cast<int>(record_write_mode));
Emit(code, 0, nullptr, input_count, inputs, temp_count, temps);
} else { } else {
InstructionOperand addr_reg = g.TempRegister(); ArchOpcode opcode;
Emit(kMipsAdd | AddressingModeField::encode(kMode_None), addr_reg, switch (rep) {
g.UseRegister(index), g.UseRegister(base)); case kRepFloat32:
// Emit desired store opcode, using temp addr_reg. opcode = kMipsSwc1;
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), break;
addr_reg, g.TempImmediate(0), g.UseRegister(value)); case kRepFloat64:
opcode = kMipsSdc1;
break;
case kRepBit: // Fall through.
case kRepWord8:
opcode = kMipsSb;
break;
case kRepWord16:
opcode = kMipsSh;
break;
case kRepTagged: // Fall through.
case kRepWord32:
opcode = kMipsSw;
break;
default:
UNREACHABLE();
return;
}
if (g.CanBeImmediate(index, opcode)) {
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value));
} else {
InstructionOperand addr_reg = g.TempRegister();
Emit(kMipsAdd | AddressingModeField::encode(kMode_None), addr_reg,
g.UseRegister(index), g.UseRegister(base));
// Emit desired store opcode, using temp addr_reg.
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
addr_reg, g.TempImmediate(0), g.UseRegister(value));
}
} }
} }
......
...@@ -213,6 +213,48 @@ class OutOfLineCeil final : public OutOfLineRound { ...@@ -213,6 +213,48 @@ class OutOfLineCeil final : public OutOfLineRound {
}; };
class OutOfLineRecordWrite final : public OutOfLineCode {
public:
OutOfLineRecordWrite(CodeGenerator* gen, Register object, Register index,
Register value, Register scratch0, Register scratch1,
RecordWriteMode mode)
: OutOfLineCode(gen),
object_(object),
index_(index),
value_(value),
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode) {}
void Generate() final {
if (mode_ > RecordWriteMode::kValueIsPointer) {
__ JumpIfSmi(value_, exit());
}
if (mode_ > RecordWriteMode::kValueIsMap) {
__ CheckPageFlag(value_, scratch0_,
MemoryChunk::kPointersToHereAreInterestingMask, eq,
exit());
}
SaveFPRegsMode const save_fp_mode =
frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
// TODO(turbofan): Once we get frame elision working, we need to save
// and restore lr properly here if the frame was elided.
RecordWriteStub stub(isolate(), object_, scratch0_, scratch1_,
EMIT_REMEMBERED_SET, save_fp_mode);
__ Addu(scratch1_, object_, index_);
__ CallStub(&stub);
}
private:
Register const object_;
Register const index_;
Register const value_;
Register const scratch0_;
Register const scratch1_;
RecordWriteMode const mode_;
};
Condition FlagsConditionToConditionCmp(FlagsCondition condition) { Condition FlagsConditionToConditionCmp(FlagsCondition condition) {
switch (condition) { switch (condition) {
case kEqual: case kEqual:
...@@ -518,6 +560,24 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -518,6 +560,24 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
case kArchTruncateDoubleToI: case kArchTruncateDoubleToI:
__ TruncateDoubleToI(i.OutputRegister(), i.InputDoubleRegister(0)); __ TruncateDoubleToI(i.OutputRegister(), i.InputDoubleRegister(0));
break; break;
case kArchStoreWithWriteBarrier: {
RecordWriteMode mode =
static_cast<RecordWriteMode>(MiscField::decode(instr->opcode()));
Register object = i.InputRegister(0);
Register index = i.InputRegister(1);
Register value = i.InputRegister(2);
Register scratch0 = i.TempRegister(0);
Register scratch1 = i.TempRegister(1);
auto ool = new (zone()) OutOfLineRecordWrite(this, object, index, value,
scratch0, scratch1, mode);
__ Daddu(at, object, index);
__ sd(value, MemOperand(at));
__ CheckPageFlag(object, scratch0,
MemoryChunk::kPointersFromHereAreInterestingMask, ne,
ool->entry());
__ bind(ool->exit());
break;
}
case kMips64Add: case kMips64Add:
__ Addu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); __ Addu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break; break;
...@@ -967,18 +1027,6 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -967,18 +1027,6 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
} }
break; break;
} }
case kMips64StoreWriteBarrier: {
Register object = i.InputRegister(0);
Register index = i.InputRegister(1);
Register value = i.InputRegister(2);
__ daddu(index, object, index);
__ sd(value, MemOperand(index));
SaveFPRegsMode mode =
frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
RAStatus ra_status = kRAHasNotBeenSaved;
__ RecordWrite(object, index, value, ra_status, mode);
break;
}
case kCheckedLoadInt8: case kCheckedLoadInt8:
ASSEMBLE_CHECKED_LOAD_INTEGER(lb); ASSEMBLE_CHECKED_LOAD_INTEGER(lb);
break; break;
......
...@@ -102,8 +102,7 @@ namespace compiler { ...@@ -102,8 +102,7 @@ namespace compiler {
V(Mips64Float32Min) \ V(Mips64Float32Min) \
V(Mips64Push) \ V(Mips64Push) \
V(Mips64StoreToStackSlot) \ V(Mips64StoreToStackSlot) \
V(Mips64StackClaim) \ V(Mips64StackClaim)
V(Mips64StoreWriteBarrier)
// Addressing modes represent the "shape" of inputs to an instruction. // Addressing modes represent the "shape" of inputs to an instruction.
......
...@@ -186,56 +186,78 @@ void InstructionSelector::VisitStore(Node* node) { ...@@ -186,56 +186,78 @@ void InstructionSelector::VisitStore(Node* node) {
Node* value = node->InputAt(2); Node* value = node->InputAt(2);
StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node); StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node);
WriteBarrierKind write_barrier_kind = store_rep.write_barrier_kind();
MachineType rep = RepresentationOf(store_rep.machine_type()); MachineType rep = RepresentationOf(store_rep.machine_type());
if (store_rep.write_barrier_kind() == kFullWriteBarrier) {
DCHECK(rep == kRepTagged);
// TODO(dcarney): refactor RecordWrite function to take temp registers
// and pass them here instead of using fixed regs
// TODO(dcarney): handle immediate indices.
InstructionOperand temps[] = {g.TempRegister(t1), g.TempRegister(t2)};
Emit(kMips64StoreWriteBarrier, g.NoOutput(), g.UseFixed(base, t0),
g.UseFixed(index, t1), g.UseFixed(value, t2), arraysize(temps), temps);
return;
}
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind());
ArchOpcode opcode; // TODO(mips): I guess this could be done in a better way.
switch (rep) { if (write_barrier_kind != kNoWriteBarrier) {
case kRepFloat32: DCHECK_EQ(kRepTagged, rep);
opcode = kMips64Swc1; InstructionOperand inputs[3];
break; size_t input_count = 0;
case kRepFloat64: inputs[input_count++] = g.UseUniqueRegister(base);
opcode = kMips64Sdc1; inputs[input_count++] = g.UseUniqueRegister(index);
break; inputs[input_count++] = (write_barrier_kind == kMapWriteBarrier)
case kRepBit: // Fall through. ? g.UseRegister(value)
case kRepWord8: : g.UseUniqueRegister(value);
opcode = kMips64Sb; RecordWriteMode record_write_mode = RecordWriteMode::kValueIsAny;
break; switch (write_barrier_kind) {
case kRepWord16: case kNoWriteBarrier:
opcode = kMips64Sh; UNREACHABLE();
break; break;
case kRepWord32: case kMapWriteBarrier:
opcode = kMips64Sw; record_write_mode = RecordWriteMode::kValueIsMap;
break; break;
case kRepTagged: // Fall through. case kPointerWriteBarrier:
case kRepWord64: record_write_mode = RecordWriteMode::kValueIsPointer;
opcode = kMips64Sd; break;
break; case kFullWriteBarrier:
default: record_write_mode = RecordWriteMode::kValueIsAny;
UNREACHABLE(); break;
return; }
} InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
size_t const temp_count = arraysize(temps);
if (g.CanBeImmediate(index, opcode)) { InstructionCode code = kArchStoreWithWriteBarrier;
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), code |= MiscField::encode(static_cast<int>(record_write_mode));
g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value)); Emit(code, 0, nullptr, input_count, inputs, temp_count, temps);
} else { } else {
InstructionOperand addr_reg = g.TempRegister(); ArchOpcode opcode;
Emit(kMips64Dadd | AddressingModeField::encode(kMode_None), addr_reg, switch (rep) {
g.UseRegister(index), g.UseRegister(base)); case kRepFloat32:
// Emit desired store opcode, using temp addr_reg. opcode = kMips64Swc1;
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), break;
addr_reg, g.TempImmediate(0), g.UseRegister(value)); case kRepFloat64:
opcode = kMips64Sdc1;
break;
case kRepBit: // Fall through.
case kRepWord8:
opcode = kMips64Sb;
break;
case kRepWord16:
opcode = kMips64Sh;
break;
case kRepWord32:
opcode = kMips64Sw;
break;
case kRepTagged: // Fall through.
case kRepWord64:
opcode = kMips64Sd;
break;
default:
UNREACHABLE();
return;
}
if (g.CanBeImmediate(index, opcode)) {
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value));
} else {
InstructionOperand addr_reg = g.TempRegister();
Emit(kMips64Dadd | AddressingModeField::encode(kMode_None), addr_reg,
g.UseRegister(index), g.UseRegister(base));
// Emit desired store opcode, using temp addr_reg.
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
addr_reg, g.TempImmediate(0), g.UseRegister(value));
}
} }
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <limits> #include <limits>
#include "src/address-map.h"
#include "src/base/bits.h" #include "src/base/bits.h"
#include "src/code-factory.h" #include "src/code-factory.h"
#include "src/compiler/common-operator.h" #include "src/compiler/common-operator.h"
...@@ -1151,8 +1152,35 @@ WriteBarrierKind ComputeWriteBarrierKind(BaseTaggedness base_is_tagged, ...@@ -1151,8 +1152,35 @@ WriteBarrierKind ComputeWriteBarrierKind(BaseTaggedness base_is_tagged,
// Write barriers are only for writes of heap objects. // Write barriers are only for writes of heap objects.
return kNoWriteBarrier; return kNoWriteBarrier;
} }
if (input_type->Is(Type::BooleanOrNullOrUndefined())) {
// Write barriers are not necessary when storing true, false, null or
// undefined, because these special oddballs are always in the root set.
return kNoWriteBarrier;
}
if (base_is_tagged == kTaggedBase && if (base_is_tagged == kTaggedBase &&
RepresentationOf(representation) == kRepTagged) { RepresentationOf(representation) == kRepTagged) {
if (input_type->IsConstant() &&
input_type->AsConstant()->Value()->IsHeapObject()) {
Handle<HeapObject> input =
Handle<HeapObject>::cast(input_type->AsConstant()->Value());
if (input->IsMap()) {
// Write barriers for storing maps are cheaper.
return kMapWriteBarrier;
}
Isolate* const isolate = input->GetIsolate();
RootIndexMap root_index_map(isolate);
int root_index = root_index_map.Lookup(*input);
if (root_index != RootIndexMap::kInvalidRootIndex &&
isolate->heap()->RootIsImmortalImmovable(root_index)) {
// Write barriers are unnecessary for immortal immovable roots.
return kNoWriteBarrier;
}
}
if (field_type->Is(Type::TaggedPointer()) ||
input_type->Is(Type::TaggedPointer())) {
// Write barriers for heap objects don't need a Smi check.
return kPointerWriteBarrier;
}
// Write barriers are only for writes into heap objects (i.e. tagged base). // Write barriers are only for writes into heap objects (i.e. tagged base).
return kFullWriteBarrier; return kFullWriteBarrier;
} }
......
...@@ -191,6 +191,46 @@ class OutOfLineTruncateDoubleToI final : public OutOfLineCode { ...@@ -191,6 +191,46 @@ class OutOfLineTruncateDoubleToI final : public OutOfLineCode {
XMMRegister const input_; XMMRegister const input_;
}; };
class OutOfLineRecordWrite final : public OutOfLineCode {
public:
OutOfLineRecordWrite(CodeGenerator* gen, Register object, Operand operand,
Register value, Register scratch0, Register scratch1,
RecordWriteMode mode)
: OutOfLineCode(gen),
object_(object),
operand_(operand),
value_(value),
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode) {}
void Generate() final {
if (mode_ > RecordWriteMode::kValueIsPointer) {
__ JumpIfSmi(value_, exit());
}
if (mode_ > RecordWriteMode::kValueIsMap) {
__ CheckPageFlag(value_, scratch0_,
MemoryChunk::kPointersToHereAreInterestingMask, zero,
exit());
}
SaveFPRegsMode const save_fp_mode =
frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
RecordWriteStub stub(isolate(), object_, scratch0_, scratch1_,
EMIT_REMEMBERED_SET, save_fp_mode);
__ leap(scratch1_, operand_);
__ CallStub(&stub);
}
private:
Register const object_;
Operand const operand_;
Register const value_;
Register const scratch0_;
Register const scratch1_;
RecordWriteMode const mode_;
};
} // namespace } // namespace
...@@ -654,6 +694,24 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -654,6 +694,24 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ bind(ool->exit()); __ bind(ool->exit());
break; break;
} }
case kArchStoreWithWriteBarrier: {
RecordWriteMode mode =
static_cast<RecordWriteMode>(MiscField::decode(instr->opcode()));
Register object = i.InputRegister(0);
size_t index = 0;
Operand operand = i.MemoryOperand(&index);
Register value = i.InputRegister(index);
Register scratch0 = i.TempRegister(0);
Register scratch1 = i.TempRegister(1);
auto ool = new (zone()) OutOfLineRecordWrite(this, object, operand, value,
scratch0, scratch1, mode);
__ movp(operand, value);
__ CheckPageFlag(object, scratch0,
MemoryChunk::kPointersFromHereAreInterestingMask,
not_zero, ool->entry());
__ bind(ool->exit());
break;
}
case kX64Add32: case kX64Add32:
ASSEMBLE_BINOP(addl); ASSEMBLE_BINOP(addl);
break; break;
...@@ -1306,24 +1364,6 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -1306,24 +1364,6 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
} }
break; break;
} }
case kX64StoreWriteBarrier: {
Register object = i.InputRegister(0);
Register value = i.InputRegister(2);
SaveFPRegsMode mode =
frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs;
if (HasImmediateInput(instr, 1)) {
int index = i.InputInt32(1);
Register scratch = i.TempRegister(1);
__ movq(Operand(object, index), value);
__ RecordWriteContextSlot(object, index, value, scratch, mode);
} else {
Register index = i.InputRegister(1);
__ movq(Operand(object, index, times_1, 0), value);
__ leaq(index, Operand(object, index, times_1, 0));
__ RecordWrite(object, index, value, mode);
}
break;
}
case kCheckedLoadInt8: case kCheckedLoadInt8:
ASSEMBLE_CHECKED_LOAD_INTEGER(movsxbl); ASSEMBLE_CHECKED_LOAD_INTEGER(movsxbl);
break; break;
......
...@@ -124,7 +124,6 @@ namespace compiler { ...@@ -124,7 +124,6 @@ namespace compiler {
V(X64Inc32) \ V(X64Inc32) \
V(X64Push) \ V(X64Push) \
V(X64Poke) \ V(X64Poke) \
V(X64StoreWriteBarrier) \
V(X64StackCheck) V(X64StackCheck)
......
...@@ -157,61 +157,84 @@ void InstructionSelector::VisitStore(Node* node) { ...@@ -157,61 +157,84 @@ void InstructionSelector::VisitStore(Node* node) {
Node* value = node->InputAt(2); Node* value = node->InputAt(2);
StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node); StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node);
WriteBarrierKind write_barrier_kind = store_rep.write_barrier_kind();
MachineType rep = RepresentationOf(store_rep.machine_type()); MachineType rep = RepresentationOf(store_rep.machine_type());
if (store_rep.write_barrier_kind() == kFullWriteBarrier) {
if (write_barrier_kind != kNoWriteBarrier) {
DCHECK_EQ(kRepTagged, rep); DCHECK_EQ(kRepTagged, rep);
// TODO(dcarney): refactor RecordWrite function to take temp registers AddressingMode addressing_mode;
// and pass them here instead of using fixed regs InstructionOperand inputs[3];
size_t input_count = 0;
inputs[input_count++] = g.UseUniqueRegister(base);
if (g.CanBeImmediate(index)) { if (g.CanBeImmediate(index)) {
InstructionOperand temps[] = {g.TempRegister(rcx), g.TempRegister()}; inputs[input_count++] = g.UseImmediate(index);
Emit(kX64StoreWriteBarrier, g.NoOutput(), g.UseFixed(base, rbx), addressing_mode = kMode_MRI;
g.UseImmediate(index), g.UseFixed(value, rcx), arraysize(temps),
temps);
} else { } else {
InstructionOperand temps[] = {g.TempRegister(rcx), g.TempRegister(rdx)}; inputs[input_count++] = g.UseUniqueRegister(index);
Emit(kX64StoreWriteBarrier, g.NoOutput(), g.UseFixed(base, rbx), addressing_mode = kMode_MR1;
g.UseFixed(index, rcx), g.UseFixed(value, rdx), arraysize(temps),
temps);
} }
return; inputs[input_count++] = (write_barrier_kind == kMapWriteBarrier)
} ? g.UseRegister(value)
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind()); : g.UseUniqueRegister(value);
RecordWriteMode record_write_mode = RecordWriteMode::kValueIsAny;
ArchOpcode opcode; switch (write_barrier_kind) {
switch (rep) { case kNoWriteBarrier:
case kRepFloat32: UNREACHABLE();
opcode = kX64Movss; break;
break; case kMapWriteBarrier:
case kRepFloat64: record_write_mode = RecordWriteMode::kValueIsMap;
opcode = kX64Movsd; break;
break; case kPointerWriteBarrier:
case kRepBit: // Fall through. record_write_mode = RecordWriteMode::kValueIsPointer;
case kRepWord8: break;
opcode = kX64Movb; case kFullWriteBarrier:
break; record_write_mode = RecordWriteMode::kValueIsAny;
case kRepWord16: break;
opcode = kX64Movw; }
break; InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
case kRepWord32: size_t const temp_count = arraysize(temps);
opcode = kX64Movl; InstructionCode code = kArchStoreWithWriteBarrier;
break; code |= AddressingModeField::encode(addressing_mode);
case kRepTagged: // Fall through. code |= MiscField::encode(static_cast<int>(record_write_mode));
case kRepWord64: Emit(code, 0, nullptr, input_count, inputs, temp_count, temps);
opcode = kX64Movq; } else {
break; ArchOpcode opcode;
default: switch (rep) {
UNREACHABLE(); case kRepFloat32:
return; opcode = kX64Movss;
break;
case kRepFloat64:
opcode = kX64Movsd;
break;
case kRepBit: // Fall through.
case kRepWord8:
opcode = kX64Movb;
break;
case kRepWord16:
opcode = kX64Movw;
break;
case kRepWord32:
opcode = kX64Movl;
break;
case kRepTagged: // Fall through.
case kRepWord64:
opcode = kX64Movq;
break;
default:
UNREACHABLE();
return;
}
InstructionOperand inputs[4];
size_t input_count = 0;
AddressingMode addressing_mode =
g.GetEffectiveAddressMemoryOperand(node, inputs, &input_count);
InstructionCode code =
opcode | AddressingModeField::encode(addressing_mode);
InstructionOperand value_operand =
g.CanBeImmediate(value) ? g.UseImmediate(value) : g.UseRegister(value);
inputs[input_count++] = value_operand;
Emit(code, 0, static_cast<InstructionOperand*>(NULL), input_count, inputs);
} }
InstructionOperand inputs[4];
size_t input_count = 0;
AddressingMode mode =
g.GetEffectiveAddressMemoryOperand(node, inputs, &input_count);
InstructionCode code = opcode | AddressingModeField::encode(mode);
InstructionOperand value_operand =
g.CanBeImmediate(value) ? g.UseImmediate(value) : g.UseRegister(value);
inputs[input_count++] = value_operand;
Emit(code, 0, static_cast<InstructionOperand*>(NULL), input_count, inputs);
} }
......
...@@ -4380,11 +4380,16 @@ bool Heap::IsValidAllocationSpace(AllocationSpace space) { ...@@ -4380,11 +4380,16 @@ bool Heap::IsValidAllocationSpace(AllocationSpace space) {
bool Heap::RootIsImmortalImmovable(int root_index) { bool Heap::RootIsImmortalImmovable(int root_index) {
switch (root_index) { switch (root_index) {
#define CASE(name) \ #define IMMORTAL_IMMOVABLE_ROOT(name) case Heap::k##name##RootIndex:
case Heap::k##name##RootIndex: \ IMMORTAL_IMMOVABLE_ROOT_LIST(IMMORTAL_IMMOVABLE_ROOT)
#undef IMMORTAL_IMMOVABLE_ROOT
#define INTERNALIZED_STRING(name, value) case Heap::k##name##RootIndex:
INTERNALIZED_STRING_LIST(INTERNALIZED_STRING)
#undef INTERNALIZED_STRING
#define STRING_TYPE(NAME, size, name, Name) case Heap::k##Name##MapRootIndex:
STRING_TYPE_LIST(STRING_TYPE)
#undef STRING_TYPE
return true; return true;
IMMORTAL_IMMOVABLE_ROOT_LIST(CASE);
#undef CASE
default: default:
return false; return false;
} }
......
...@@ -208,34 +208,36 @@ namespace internal { ...@@ -208,34 +208,36 @@ namespace internal {
V(Function, 1u << 19 | REPRESENTATION(kTaggedPointer)) \ V(Function, 1u << 19 | REPRESENTATION(kTaggedPointer)) \
V(Internal, 1u << 20 | REPRESENTATION(kTagged | kUntagged)) \ V(Internal, 1u << 20 | REPRESENTATION(kTagged | kUntagged)) \
\ \
V(Signed31, kUnsigned30 | kNegative31) \ V(Signed31, kUnsigned30 | kNegative31) \
V(Signed32, kSigned31 | kOtherUnsigned31 | kOtherSigned32) \ V(Signed32, kSigned31 | kOtherUnsigned31 | kOtherSigned32) \
V(Negative32, kNegative31 | kOtherSigned32) \ V(Negative32, kNegative31 | kOtherSigned32) \
V(Unsigned31, kUnsigned30 | kOtherUnsigned31) \ V(Unsigned31, kUnsigned30 | kOtherUnsigned31) \
V(Unsigned32, kUnsigned30 | kOtherUnsigned31 | kOtherUnsigned32) \ V(Unsigned32, kUnsigned30 | kOtherUnsigned31 | \
V(Integral32, kSigned32 | kUnsigned32) \ kOtherUnsigned32) \
V(PlainNumber, kIntegral32 | kOtherNumber) \ V(Integral32, kSigned32 | kUnsigned32) \
V(OrderedNumber, kPlainNumber | kMinusZero) \ V(PlainNumber, kIntegral32 | kOtherNumber) \
V(MinusZeroOrNaN, kMinusZero | kNaN) \ V(OrderedNumber, kPlainNumber | kMinusZero) \
V(Number, kOrderedNumber | kNaN) \ V(MinusZeroOrNaN, kMinusZero | kNaN) \
V(String, kInternalizedString | kOtherString) \ V(Number, kOrderedNumber | kNaN) \
V(UniqueName, kSymbol | kInternalizedString) \ V(String, kInternalizedString | kOtherString) \
V(Name, kSymbol | kString) \ V(UniqueName, kSymbol | kInternalizedString) \
V(BooleanOrNumber, kBoolean | kNumber) \ V(Name, kSymbol | kString) \
V(NullOrUndefined, kNull | kUndefined) \ V(BooleanOrNumber, kBoolean | kNumber) \
V(NumberOrString, kNumber | kString) \ V(BooleanOrNullOrUndefined, kBoolean | kNull | kUndefined) \
V(NumberOrUndefined, kNumber | kUndefined) \ V(NullOrUndefined, kNull | kUndefined) \
V(PlainPrimitive, kNumberOrString | kBoolean | kNullOrUndefined) \ V(NumberOrString, kNumber | kString) \
V(Primitive, kSymbol | kSimd | kPlainPrimitive) \ V(NumberOrUndefined, kNumber | kUndefined) \
V(DetectableReceiver, kFunction | kOtherObject | kProxy) \ V(PlainPrimitive, kNumberOrString | kBoolean | kNullOrUndefined) \
V(Detectable, kDetectableReceiver | kNumber | kName) \ V(Primitive, kSymbol | kSimd | kPlainPrimitive) \
V(Object, kFunction | kOtherObject | kUndetectable) \ V(DetectableReceiver, kFunction | kOtherObject | kProxy) \
V(Receiver, kObject | kProxy) \ V(Detectable, kDetectableReceiver | kNumber | kName) \
V(StringOrReceiver, kString | kReceiver) \ V(Object, kFunction | kOtherObject | kUndetectable) \
V(Unique, kBoolean | kUniqueName | kNull | kUndefined | \ V(Receiver, kObject | kProxy) \
kReceiver) \ V(StringOrReceiver, kString | kReceiver) \
V(NonNumber, kUnique | kString | kInternal) \ V(Unique, kBoolean | kUniqueName | kNull | kUndefined | \
V(Any, 0xfffffffeu) kReceiver) \
V(NonNumber, kUnique | kString | kInternal) \
V(Any, 0xfffffffeu)
// clang-format on // clang-format on
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment