Skip to content

Commit 7d3ecd2

Browse files
rmacnak-googleCommit Queue
authored andcommitted
[vm, compiler] Reduce spilling needed to call __tsan_read/write.
Make the Dart register allocator spill what is needed instead of spilling all volatile registers. TEST=tsan Bug: #61352 Change-Id: Ic50937e305698565a14469181b1e5f408104c656 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/445964 Reviewed-by: Alexander Aprelev <[email protected]> Commit-Queue: Ryan Macnak <[email protected]>
1 parent 39d2b20 commit 7d3ecd2

File tree

7 files changed

+198
-22
lines changed

7 files changed

+198
-22
lines changed

runtime/vm/compiler/backend/constant_propagator.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,8 @@ void ConstantPropagator::VisitDeoptimize(DeoptimizeInstr* instr) {
307307
// TODO(vegorov) remove all code after DeoptimizeInstr as dead.
308308
}
309309

310+
void ConstantPropagator::VisitTsanReadWrite(TsanReadWriteInstr* instr) {}
311+
310312
Definition* ConstantPropagator::UnwrapPhi(Definition* defn) {
311313
if (defn->IsPhi()) {
312314
JoinEntryInstr* block = defn->AsPhi()->block();

runtime/vm/compiler/backend/flow_graph.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2673,6 +2673,36 @@ void FlowGraph::ExtractNonInternalTypedDataPayloads() {
26732673
}
26742674
}
26752675

2676+
void FlowGraph::AddTsanInstrumentation() {
2677+
if (!FLAG_target_thread_sanitizer) return;
2678+
2679+
for (BlockIterator block_it = reverse_postorder_iterator(); !block_it.Done();
2680+
block_it.Advance()) {
2681+
BlockEntryInstr* block = block_it.Current();
2682+
for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
2683+
Instruction* current = it.Current();
2684+
2685+
if (LoadFieldInstr* load = current->AsLoadField()) {
2686+
if (!load->slot().is_no_sanitize_thread() &&
2687+
load->memory_order() == compiler::Assembler::kRelaxedNonAtomic) {
2688+
auto* tsan_read = new (Z) TsanReadWriteInstr(
2689+
TsanReadWriteInstr::Kind::kRead, load->slot(),
2690+
new (Z) Value(load->instance()->definition()), load->source());
2691+
InsertBefore(load, tsan_read, /*env=*/nullptr, kEffect);
2692+
}
2693+
} else if (StoreFieldInstr* store = current->AsStoreField()) {
2694+
if (!store->slot().is_no_sanitize_thread() &&
2695+
store->memory_order() == compiler::Assembler::kRelaxedNonAtomic) {
2696+
auto* tsan_write = new (Z) TsanReadWriteInstr(
2697+
TsanReadWriteInstr::Kind::kWrite, store->slot(),
2698+
new (Z) Value(store->instance()->definition()), store->source());
2699+
InsertBefore(store, tsan_write, /*env=*/nullptr, kEffect);
2700+
}
2701+
}
2702+
}
2703+
}
2704+
}
2705+
26762706
bool FlowGraph::Canonicalize() {
26772707
bool changed = false;
26782708

runtime/vm/compiler/backend/flow_graph.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,8 @@ class FlowGraph : public ZoneAllocated {
531531
// Once this is done, no intra-block code motion should be performed.
532532
void ExtractNonInternalTypedDataPayloads();
533533

534+
void AddTsanInstrumentation();
535+
534536
bool IsReceiver(Definition* def) const;
535537

536538
// Optimize (a << b) & c pattern: if c is a positive Smi or zero, then the

runtime/vm/compiler/backend/il.cc

Lines changed: 85 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4684,16 +4684,6 @@ void LoadFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
46844684
ASSERT(OffsetInBytes() != 0 || slot().has_untagged_instance());
46854685
auto const rep = slot().representation();
46864686

4687-
#if defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_RISCV64) || \
4688-
defined(TARGET_ARCH_X64)
4689-
if (FLAG_target_thread_sanitizer && !slot().is_no_sanitize_thread() &&
4690-
memory_order_ == compiler::Assembler::kRelaxedNonAtomic) {
4691-
intptr_t tag = slot().has_untagged_instance() ? 0 : kHeapObjectTag;
4692-
__ AddImmediate(TMP, instance_reg, slot().offset_in_bytes() - tag);
4693-
__ TsanRead(TMP, RepresentationUtils::ValueSize(rep));
4694-
}
4695-
#endif
4696-
46974687
if (calls_initializer()) {
46984688
__ LoadFromSlot(locs()->out(0).reg(), instance_reg, slot(), memory_order_);
46994689
EmitNativeCodeForInitializerCall(compiler);
@@ -7293,6 +7283,91 @@ Definition* InvokeMathCFunctionInstr::Canonicalize(FlowGraph* flow_graph) {
72937283
return this;
72947284
}
72957285

7286+
LocationSummary* TsanReadWriteInstr::MakeLocationSummary(Zone* zone,
7287+
bool opt) const {
7288+
const intptr_t kNumTemps = 1;
7289+
LocationSummary* result = new (zone) LocationSummary(
7290+
zone, InputCount(), kNumTemps, LocationSummary::kNativeLeafCall);
7291+
result->set_temp(0, Location::RegisterLocation(CALLEE_SAVED_TEMP));
7292+
result->set_in(0, Location::RequiresRegister());
7293+
return result;
7294+
}
7295+
7296+
const RuntimeEntry& TsanReadWriteInstr::RuntimeEntry() const {
7297+
intptr_t size = RepresentationUtils::ValueSize(slot().representation());
7298+
if (kind_ == Kind::kRead) {
7299+
switch (size) {
7300+
case 1:
7301+
return kTsanRead1RuntimeEntry;
7302+
case 2:
7303+
return kTsanRead2RuntimeEntry;
7304+
case 4:
7305+
return kTsanRead4RuntimeEntry;
7306+
case 8:
7307+
return kTsanRead8RuntimeEntry;
7308+
case 16:
7309+
return kTsanRead16RuntimeEntry;
7310+
default:
7311+
UNREACHABLE();
7312+
}
7313+
} else if (kind_ == kWrite) {
7314+
switch (size) {
7315+
case 1:
7316+
return kTsanWrite1RuntimeEntry;
7317+
case 2:
7318+
return kTsanWrite2RuntimeEntry;
7319+
case 4:
7320+
return kTsanWrite4RuntimeEntry;
7321+
case 8:
7322+
return kTsanWrite8RuntimeEntry;
7323+
case 16:
7324+
return kTsanWrite16RuntimeEntry;
7325+
default:
7326+
UNREACHABLE();
7327+
}
7328+
}
7329+
UNREACHABLE();
7330+
}
7331+
7332+
void TsanReadWriteInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
7333+
#if defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_RISCV64) || \
7334+
defined(TARGET_ARCH_X64)
7335+
const Register saved_sp = locs()->temp(0).reg();
7336+
ASSERT(IsAbiPreservedRegister(saved_sp));
7337+
ASSERT(IsAbiPreservedRegister(THR));
7338+
ASSERT(IsAbiPreservedRegister(PP));
7339+
ASSERT(IsAbiPreservedRegister(CODE_REG));
7340+
#if defined(TARGET_ARCH_ARM64)
7341+
ASSERT(IsAbiPreservedRegister(NULL_REG));
7342+
ASSERT(IsAbiPreservedRegister(HEAP_BITS));
7343+
ASSERT(IsAbiPreservedRegister(DISPATCH_TABLE_REG));
7344+
#endif
7345+
__ MoveRegister(saved_sp, SPREG);
7346+
#if defined(TARGET_ARCH_ARM64)
7347+
__ andi(CSP, SP, compiler::Immediate(~(OS::ActivationFrameAlignment() - 1)));
7348+
#else
7349+
__ ReserveAlignedFrameSpace(0);
7350+
#endif
7351+
const Register instance_reg = locs()->in(0).reg();
7352+
intptr_t tag = slot().has_untagged_instance() ? 0 : kHeapObjectTag;
7353+
__ AddImmediate(CallingConventions::ArgumentRegisters[0], instance_reg,
7354+
slot().offset_in_bytes() - tag);
7355+
__ Load(TMP, compiler::Address(THR, RuntimeEntry().OffsetFromThread()));
7356+
__ Store(TMP,
7357+
compiler::Address(THR, compiler::target::Thread::vm_tag_offset()));
7358+
__ CallCFunction(TMP);
7359+
__ LoadImmediate(TMP, VMTag::kDartTagId);
7360+
__ Store(TMP,
7361+
compiler::Address(THR, compiler::target::Thread::vm_tag_offset()));
7362+
__ MoveRegister(SPREG, saved_sp);
7363+
#if defined(TARGET_ARCH_ARM64)
7364+
__ SetupCSPFromThread(THR);
7365+
#endif
7366+
#else
7367+
UNREACHABLE();
7368+
#endif
7369+
}
7370+
72967371
bool DoubleToIntegerInstr::SupportsFloorAndCeil() {
72977372
#if defined(TARGET_ARCH_X64)
72987373
return CompilerState::Current().is_aot() || FLAG_target_unknown_cpu;
@@ -7821,16 +7896,6 @@ void StoreFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
78217896
ASSERT(OffsetInBytes() != 0 || slot().has_untagged_instance());
78227897
const Representation rep = slot().representation();
78237898

7824-
#if defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_RISCV64) || \
7825-
defined(TARGET_ARCH_X64)
7826-
if (FLAG_target_thread_sanitizer && !slot().is_no_sanitize_thread() &&
7827-
memory_order_ == compiler::Assembler::kRelaxedNonAtomic) {
7828-
intptr_t tag = slot().has_untagged_instance() ? 0 : kHeapObjectTag;
7829-
__ AddImmediate(TMP, instance_reg, slot().offset_in_bytes() - tag);
7830-
__ TsanWrite(TMP, RepresentationUtils::ValueSize(rep));
7831-
}
7832-
#endif
7833-
78347899
#if defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_RISCV32) || \
78357900
defined(TARGET_ARCH_RISCV64)
78367901
if (locs()->in(kValuePos).IsConstant() &&

runtime/vm/compiler/backend/il.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,7 @@ struct InstrAttrs {
512512
M(OneByteStringFromCharCode, kNoGC) \
513513
M(Utf8Scan, kNoGC) \
514514
M(InvokeMathCFunction, kNoGC) \
515+
M(TsanReadWrite, kNoGC) \
515516
M(TruncDivMod, kNoGC) \
516517
/*We could be more precise about when these 2 instructions can trigger GC.*/ \
517518
M(GuardFieldClass, _) \
@@ -6422,6 +6423,9 @@ class StoreFieldInstr : public TemplateInstruction<2, NoThrow> {
64226423

64236424
Value* instance() const { return inputs_[kInstancePos]; }
64246425
const Slot& slot() const { return slot_; }
6426+
compiler::Assembler::MemoryOrder memory_order() const {
6427+
return memory_order_;
6428+
}
64256429
Value* value() const { return inputs_[kValuePos]; }
64266430

64276431
virtual TokenPosition token_pos() const { return token_pos_; }
@@ -8168,6 +8172,9 @@ class LoadFieldInstr : public TemplateLoadField<1> {
81688172

81698173
Value* instance() const { return inputs_[0]; }
81708174
const Slot& slot() const { return slot_; }
8175+
compiler::Assembler::MemoryOrder memory_order() const {
8176+
return memory_order_;
8177+
}
81718178

81728179
InnerPointerAccess loads_inner_pointer() const {
81738180
return loads_inner_pointer_;
@@ -10031,6 +10038,64 @@ class InvokeMathCFunctionInstr : public VariadicDefinition {
1003110038
DISALLOW_COPY_AND_ASSIGN(InvokeMathCFunctionInstr);
1003210039
};
1003310040

10041+
class TsanReadWriteInstr : public TemplateInstruction<1, NoThrow> {
10042+
public:
10043+
enum Kind { kRead, kWrite };
10044+
10045+
TsanReadWriteInstr(Kind kind,
10046+
const Slot& slot,
10047+
Value* instance,
10048+
const InstructionSource& source)
10049+
: TemplateInstruction(source, DeoptId::kNone),
10050+
kind_(kind),
10051+
slot_(slot),
10052+
token_pos_(source.token_pos) {
10053+
SetInputAt(0, instance);
10054+
}
10055+
10056+
Value* instance() const { return inputs_[0]; }
10057+
const Slot& slot() const { return slot_; }
10058+
10059+
virtual TokenPosition token_pos() const { return token_pos_; }
10060+
10061+
DECLARE_INSTRUCTION(TsanReadWrite)
10062+
10063+
virtual bool ComputeCanDeoptimize() const { return false; }
10064+
10065+
virtual Representation RequiredInputRepresentation(intptr_t idx) const {
10066+
ASSERT_EQUAL(idx, 0);
10067+
return slot_.has_untagged_instance() ? kUntagged : kTagged;
10068+
}
10069+
10070+
virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); }
10071+
10072+
virtual bool AllowsCSE() const { return false; }
10073+
virtual bool HasUnknownSideEffects() const { return false; }
10074+
10075+
virtual bool AttributesEqual(const Instruction& other) const {
10076+
return &other == this;
10077+
}
10078+
10079+
virtual bool MayThrow() const { return false; }
10080+
10081+
PRINT_OPERANDS_TO_SUPPORT
10082+
10083+
#define FIELD_LIST(F) \
10084+
F(const TsanReadWriteInstr::Kind, kind_) \
10085+
F(const Slot&, slot_) \
10086+
F(const TokenPosition, token_pos_)
10087+
10088+
DECLARE_INSTRUCTION_SERIALIZABLE_FIELDS(TsanReadWriteInstr,
10089+
TemplateInstruction,
10090+
FIELD_LIST)
10091+
#undef FIELD_LIST
10092+
10093+
const RuntimeEntry& RuntimeEntry() const;
10094+
10095+
private:
10096+
DISALLOW_COPY_AND_ASSIGN(TsanReadWriteInstr);
10097+
};
10098+
1003410099
class ExtractNthOutputInstr : public TemplateDefinition<1, NoThrow, Pure> {
1003510100
public:
1003610101
// Extract the Nth output register from value.

runtime/vm/compiler/backend/il_printer.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,6 +1237,16 @@ void InvokeMathCFunctionInstr::PrintOperandsTo(BaseTextBuffer* f) const {
12371237
Definition::PrintOperandsTo(f);
12381238
}
12391239

1240+
void TsanReadWriteInstr::PrintOperandsTo(BaseTextBuffer* f) const {
1241+
instance()->PrintTo(f);
1242+
f->Printf(" . %s", slot().Name());
1243+
if (kind_ == kRead) {
1244+
f->AddString(", read");
1245+
} else {
1246+
f->AddString(", write");
1247+
}
1248+
}
1249+
12401250
void GraphEntryInstr::PrintBlockHeaderTo(BaseTextBuffer* f) const {
12411251
f->Printf("B%" Pd "[graph]:%" Pd, block_id(), GetDeoptId());
12421252
}

runtime/vm/compiler/compiler_pass.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,8 +562,10 @@ COMPILER_PASS(TestILSerialization, {
562562
}
563563
});
564564

565-
COMPILER_PASS(LoweringAfterCodeMotionDisabled,
566-
{ flow_graph->ExtractNonInternalTypedDataPayloads(); });
565+
COMPILER_PASS(LoweringAfterCodeMotionDisabled, {
566+
flow_graph->ExtractNonInternalTypedDataPayloads();
567+
flow_graph->AddTsanInstrumentation();
568+
});
567569

568570
COMPILER_PASS(GenerateCode, { state->graph_compiler->CompileGraph(); });
569571

0 commit comments

Comments
 (0)