dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.18k stars 1.56k forks source link

[vm/ffi] gen_snapshot on arm 32 seg faults (release) / hits asserts (debug) #39885

Closed dcharkes closed 4 years ago

dcharkes commented 4 years ago

Creating Dart SDK issue to track https://github.com/flutter/flutter/issues/47454 (see for repro).

Most likely caused by use of FFI (reported as FFI issue), and crashes in IntConverterInstr which is used in FFI IL code.

Reproduces with gen_snapshot from Dart SDK with the dill file from the original repro:

$ ~/master/sdk/out/DebugAndroidARM/clang_x86/gen_snapshot --causal_async_stacks --deterministic --snapshot_kind=app-aot-elf --elf=/usr/local/google/home/dacoharkes/ffi_samples/vaind/objectbox-dart/example/flutter/objectbox_demo/.dart_tool/flutter_build/3aee09320b7d0ee996d0dc6e2e240f25/armeabi-v7a/app.so --strip --no-sim-use-hardfp --no-use-integer-division /usr/local/google/home/dacoharkes/ffi_samples/vaind/objectbox-dart/example/flutter/objectbox_demo/.dart_tool/flutter_build/3aee09320b7d0ee996d0dc6e2e240f25/app.dill
../../runtime/vm/compiler/backend/flow_graph_checker.cc: 368: error: expected: DefDominatesUse(def, instruction)
version=2.8.0-edge.4774141425f3e8999417b688962b29ae21277c10 (Fri Dec 20 10:14:10 2019 +0000) on "linux_simarm"
thread=157996, isolate=isolate(0x1a658a0)
  pc 0x00a8285c fp 0xffd896c8 dart::Profiler::DumpStackTrace(void*)
  pc 0x00fc7454 fp 0xffd896e8 Dart_DumpNativeStackTrace
  pc 0x00843dbb fp 0xffd89718 dart::Assert::Fail(char const*, ...)
  pc 0x00bc4d2f fp 0xffd89758 dart::FlowGraphChecker::VisitDefUse(dart::Definition*, dart::Value*, dart::Value*, bool)
  pc 0x00bc496b fp 0xffd89788 dart::FlowGraphChecker::VisitDefinition(dart::Definition*)
  pc 0x00bc44ec fp 0xffd897e8 dart::FlowGraphChecker::VisitInstruction(dart::Instruction*)
  pc 0x00bc40b3 fp 0xffd89838 dart::FlowGraphChecker::VisitInstructions(dart::BlockEntryInstr*)
  pc 0x00bc3b92 fp 0xffd89898 dart::FlowGraphChecker::VisitBlocks()
  pc 0x00bc55f0 fp 0xffd898c8 dart::FlowGraphChecker::Check(char const*)
  pc 0x00ca500b fp 0xffd89948 dart::CompilerPass::Run(dart::CompilerPassState*) const
  pc 0x00ca54a7 fp 0xffd89968 dart::CompilerPass::RunPipeline(dart::CompilerPass::PipelineMode, dart::CompilerPassState*)
  pc 0x00b66f4c fp 0xffd89ce8 dart::PrecompileParsedFunctionHelper::Compile(dart::CompilationPipeline*)
  pc 0x00b69381 fp 0xffd89e78 /usr/local/google/home/dacoharkes/master/sdk/out/DebugAndroidARM/clang_x86/gen_snapshot+0x769381
  pc 0x00b640ed fp 0xffd89ee8 dart::Precompiler::CompileFunction(dart::Precompiler*, dart::Thread*, dart::Zone*, dart::Function const&)
  pc 0x00b62a46 fp 0xffd89f38 dart::Precompiler::ProcessFunction(dart::Function const&)
  pc 0x00b5e842 fp 0xffd89f58 dart::Precompiler::Iterate()
  pc 0x00b5b968 fp 0xffd8a068 dart::Precompiler::DoCompileAll()
  pc 0x00b5b263 fp 0xffd8a2b8 dart::Precompiler::CompileAll()
  pc 0x00fc531c fp 0xffd8a338 Dart_Precompile
  pc 0x0081901b fp 0xffd8a448 dart::bin::main(int, char**)
  pc 0x00819f24 fp 0xffd8a478 main
-- End of DumpStackTrace
Aborted

Asserts in debug:

dcharkes commented 4 years ago
(gdb) p def->ToCString()
$17 = 0xf547ba70 "PushArgument(v242) T{*?}"
(gdb) p use->instruction_->ToCString()
$18 = 0xf547bad8 "B38[join try_idx 0]:185 pred(B32, B34, B36, B37) {\n      v178 <- phi(v112, v22, v43, v0) alive T{_Smi?}\n}"
~/master/sdk/out/DebugAndroidARM/clang_x86/gen_snapshot --print_environments --causal_async_stacks --deterministic --snapshot_kind=app-aot-elf --elf=/usr/local/google/home/dacoharkes/ffi_samples/vaind/objectbox-dart/example/flutter/objectbox_demo/.dart_tool/flutter_build/3aee09320b7d0ee996d0dc6e2e240f25/armeabi-v7a/app.so --strip --no-sim-use-hardfp --no-use-integer-division --print-flow-graph --disassemble --compiler-passes=* /usr/local/google/home/dacoharkes/ffi_samples/vaind/objectbox-dart/example/flutter/objectbox_demo/.dart_tool/flutter_build/3aee09320b7d0ee996d0dc6e2e240f25/app.dill &> flowgraph; tail -n 10000 flowgraph &> flowgraph-short

Flow graph after applying ICData:

...
    v75 <- LoadField(v115 . _cBox@494374530) T{Pointer?}
    PushArgument(v71)
    v127 <- CheckNull:176(v71, NoSuchMethodError) T{*} env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, v71, a3, v71 }
    v77 <- StaticCall:178( get:ptr<0> v71, result_type = T{Pointer?}) T{Pointer?} env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, v0, a3 }
    PushArgument(v127 T{*})
    CheckNull:180(v127 T{*}, NoSuchMethodError) T{*} env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, v71, a4, v71 }
    v79 <- StaticCall:182( get:length<0> v127 T{*}, result_type = T{int?}) T{int?} env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, v0, a4 }
// bunch of checks
    Branch if StrictCompare:26(===, v25, v25) goto (32, 33) env={ v115, v49, v0, v49, v0, v25, v25 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
B32[target try_idx 0]:30 env={ v115, v49, v0, v49, v0 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
    goto:14 B38 env={ v115, v49, v0, v49, v0, v112 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
B33[target try_idx 0]:32 env={ v115, v49, v0, v49, v0 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
    Branch if StrictCompare:36(===, v216, v25) goto (34, 35) env={ v115, v49, v0, v49, v0, v216, v25 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
B34[target try_idx 0]:40 env={ v115, v49, v0, v49, v0 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
    goto:18 B38 env={ v115, v49, v0, v49, v0, v22 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
B35[target try_idx 0]:42 env={ v115, v49, v0, v49, v0 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
    Branch if StrictCompare:46(===, v216, v25) goto (36, 37) env={ v115, v49, v0, v49, v0, v216, v25 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
B36[target try_idx 0]:50 env={ v115, v49, v0, v49, v0 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
    goto:22 B38 env={ v115, v49, v0, v49, v0, v43 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
B37[target try_idx 0]:52 env={ v115, v49, v0, v49, v0 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
    goto:56 B38 env={ v115, v49, v0, v49, v0, v0 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
// end of bunch of checks
B38[join try_idx 0]:185 pred(B32, B34, B36, B37) { // USE. (as one of: a0, a1, a2, a3, a4)
      v178 <- phi(v112, v22, v43, v0) alive T{_Smi?}
} env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4, v1, v1, v178 }
    PushArgument(v148)
    v242 <- StaticCall:222( get:obx_box_put<0> v148) T{(Pointer<Void>, int, Pointer<Uint8>, int, int) => int?} env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0 }
    PushArgument(v242) T{*?} // DEF.
    PushArgument(v75) T{*?}
    PushArgument(v64) T{*?}
    PushArgument(v77) T{*?}
    PushArgument(v79) T{*?}
    PushArgument(v178 T{_Smi?}) T{*?}
    v83 <- InstanceCall:224( call<0>, v242, v75, v64, v77, v79, v178 T{_Smi?} IC[0: ]) T{*?} env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4, a5 }
    PushArgument(v83 T{*?})
...

The use is before the def in the flow graph?

Before applying ICData:

...
    PushArgument(v148)
    v75 <- LoadField(v115 . _cBox@494374530) T{Pointer?}
    PushArgument(v75)
    PushArgument(v64)
    PushArgument(v71)
    v127 <- CheckNull:176(v71, NoSuchMethodError) T{*} env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, v71, a3, v71 }
    v77 <- StaticCall:178( get:ptr<0> v71, result_type = T{Pointer?}) T{Pointer?} env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, v0, a3 }
    PushArgument(v77)
    PushArgument(v127 T{*})
    CheckNull:180(v127 T{*}, NoSuchMethodError) T{*} env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, v71, a4, v71 }
    v79 <- StaticCall:182( get:length<0> v127 T{*}, result_type = T{int?}) T{int?} env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, v0, a4 }
    PushArgument(v79)
// bunch of checks
    Branch if StrictCompare:26(===, v25, v25) goto (32, 33) env={ v115, v49, v0, v49, v0, v25, v25 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
B32[target try_idx 0]:30 env={ v115, v49, v0, v49, v0 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
    goto:14 B38 env={ v115, v49, v0, v49, v0, v112 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
B33[target try_idx 0]:32 env={ v115, v49, v0, v49, v0 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
    Branch if StrictCompare:36(===, v216, v25) goto (34, 35) env={ v115, v49, v0, v49, v0, v216, v25 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
B34[target try_idx 0]:40 env={ v115, v49, v0, v49, v0 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
    goto:18 B38 env={ v115, v49, v0, v49, v0, v22 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
B35[target try_idx 0]:42 env={ v115, v49, v0, v49, v0 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
    Branch if StrictCompare:46(===, v216, v25) goto (36, 37) env={ v115, v49, v0, v49, v0, v216, v25 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
B36[target try_idx 0]:50 env={ v115, v49, v0, v49, v0 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
    goto:22 B38 env={ v115, v49, v0, v49, v0, v43 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
B37[target try_idx 0]:52 env={ v115, v49, v0, v49, v0 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
    goto:56 B38 env={ v115, v49, v0, v49, v0, v0 } env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4 }
// end bunch of checks
B38[join try_idx 0]:185 pred(B32, B34, B36, B37) {
      v178 <- phi(v112, v22, v43, v0) alive T{_Smi?}
} env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4, v1, v1, v178 }
    PushArgument(v178 T{_Smi?})
    v83 <- InstanceCall:186( obx_box_put<0>, v148, v75, v64, v77, v79, v178 T{_Smi?} IC[0: ]) T{*?} env={ v36, v38, v49, v16, v0, v0, v0, v0, v50, v0, v52, v64, v68, v71, a0, a1, a2, a3, a4, a5 }
    PushArgument(v83)
...

repro:

$ ~/master/sdk/out/DebugAndroidARM/clang_x86/gen_snapshot --print_environments --causal_async_stacks --deterministic --snapshot_kind=app-aot-elf --elf=/usr/local/google/home/dacoharkes/ffi_samples/vaind/objectbox-dart/example/flutter/objectbox_demo/.dart_tool/flutter_build/3aee09320b7d0ee996d0dc6e2e240f25/armeabi-v7a/app.so --strip --no-sim-use-hardfp --no-use-integer-division --print-flow-graph --disassemble --compiler-passes=* /usr/local/google/home/dacoharkes/ffi_samples/vaind/objectbox-dart/example/flutter/objectbox_demo/.dart_tool/flutter_build/3aee09320b7d0ee996d0dc6e2e240f25/app.dill &> flowgraph; tail -n 10000 flowgraph &> flowgraph-short

The method call gets turned into a getter and function call, with an new intermediate result.

cc @mkustermann

dcharkes commented 4 years ago

Working around the flow graph checker triggers:

../../runtime/vm/compiler/backend/il.h: 8122: error: expected: to != kUntagged || (from == kUnboxedIntPtr || from == kUnboxedFfiIntPtr)

#0  0xf7fd4069 in __kernel_vsyscall ()
#1  0xf7df8c82 in raise () from /lib/i386-linux-gnu/libc.so.6
#2  0xf7de2b3d in abort () from /lib/i386-linux-gnu/libc.so.6
#3  0x00843dc8 in dart::Assert::Fail (this=0xffffbe30, format=0x65d173 "expected: %s") at ../../runtime/platform/assert.cc:44
#4  0x00bbe803 in dart::IntConverterInstr::IntConverterInstr (this=0xf5464a68, from=dart::kUnboxedInt64, to=dart::kUntagged, value=0xf5464ab8, deopt_id=-1) at ../../runtime/vm/compiler/backend/il.h:799
#5  0x00be5351 in dart::IntConverterInstr::Canonicalize (this=0xf5470e60, flow_graph=0xf54e3e90) at ../../runtime/vm/compiler/backend/il.cc:3190
#6  0x00bc007d in dart::FlowGraph::Canonicalize (this=0xf54e3e90) at ../../runtime/vm/compiler/backend/flow_graph.cc:2198
#7  0x00ca59f8 in dart::CompilerPass_Canonicalize::DoBody (this=0xfda83c <dart::compiler_pass_Canonicalize>, state=0xffffc078) at ../../runtime/vm/compiler/compiler_pass.cc:400
#8  0x00ca4f87 in dart::CompilerPass::Run (this=0xfda83c <dart::compiler_pass_Canonicalize>, state=0xffffc078) at ../../runtime/vm/compiler/compiler_pass.cc:189
#9  0x00ca4ee4 in dart::CompilerPass::Run (this=0xfda890 <dart::compiler_pass_CSE>, state=0xffffc078) at ../../runtime/vm/compiler/compiler_pass.cc:183
#10 0x00ca5536 in dart::CompilerPass::RunPipeline (mode=dart::CompilerPass::kAOT, pass_state=0xffffc078) at ../../runtime/vm/compiler/compiler_pass.cc:320
#11 0x00b66f4c in dart::PrecompileParsedFunctionHelper::Compile (this=0xffffc3d8, pipeline=0xffffc550) at ../../runtime/vm/compiler/aot/precompiler.cc:2397
#12 0x00b69381 in dart::PrecompileFunctionHelper (precompiler=<optimized out>, pipeline=<optimized out>, function=..., optimized=<optimized out>) at ../../runtime/vm/compiler/aot/precompiler.cc:2560
#13 0x00b640ed in dart::Precompiler::CompileFunction (precompiler=0xffffc7f8, thread=0xfede60, zone=<optimized out>, function=...) at ../../runtime/vm/compiler/aot/precompiler.cc:2612
#14 0x00b62a46 in dart::Precompiler::ProcessFunction (this=0xffffc7f8, function=...) at ../../runtime/vm/compiler/aot/precompiler.cc:628
#15 0x00b5e842 in dart::Precompiler::Iterate (this=0xffffc7f8) at ../../runtime/vm/compiler/aot/precompiler.cc:541
#16 0x00b5b968 in dart::Precompiler::DoCompileAll (this=0xffffc7f8) at ../../runtime/vm/compiler/aot/precompiler.cc:362
#17 0x00b5b263 in dart::Precompiler::CompileAll () at ../../runtime/vm/compiler/aot/precompiler.cc:144
#18 0x00fc52ec in Dart_Precompile () at ../../runtime/vm/dart_api_impl.cc:6109
#19 0x0081901b in dart::bin::CreateAndWritePrecompiledSnapshot () at ../../runtime/bin/gen_snapshot.cc:625
#20 dart::bin::CreateIsolateAndSnapshot (inputs=...) at ../../runtime/bin/gen_snapshot.cc:831
#21 dart::bin::main (argc=9, argv=0xffffcbd4) at ../../runtime/bin/gen_snapshot.cc:943
#22 0x00819f24 in main (argc=9, argv=0xffffcbd4) at ../../runtime/bin/gen_snapshot.cc:961

IL before the assert:

...
    v330 <- BoxInt64((v73, v74) T{int})
    v288 <- UnboxUint32([tr], v330 T{int}) T{int} // Throws away v73 basically.
    v290 <- BoxUint32(v288) T{int}
...
    v255 <- UnboxUint32([tr], v290) T{int} // Truncates again, but does nothing.
    v340 <- IntConverter(uint32->int32, v255 T{int}) // Hm, okay.
    v268 <- IntConverter(int32->untagged[tr], v340 T{int}) T{int} // Truncate does nothing.
...

It seems to coalesce two intconverter instructions

0xf5464b20 "v358 <- IntConverter(int64->int32, (v73, v74) T{int}) T{int}"
0xf5464bb0 "v268 <- IntConverter(int32->untagged[tr], v358 T{int}) T{int}"

v358 does not have truncation anymore?

The asserts in IntConverterInstr constructor do not accept the int64 -> untagged conversion:

  IntConverterInstr(Representation from,
                    Representation to,
                    Value* value,
                    intptr_t deopt_id)
      : TemplateDefinition(deopt_id),
        from_representation_(from),
        to_representation_(to),
        is_truncating_(to == kUnboxedUint32) {
    ASSERT(from != to);
    ASSERT(from == kUnboxedInt64 || from == kUnboxedUint32 ||
           from == kUnboxedInt32 || from == kUntagged);
    ASSERT(to == kUnboxedInt64 || to == kUnboxedUint32 || to == kUnboxedInt32 ||
           to == kUntagged);
    ASSERT(from != kUntagged ||
           (to == kUnboxedIntPtr || to == kUnboxedFfiIntPtr));
    ASSERT(to != kUntagged ||
           (from == kUnboxedIntPtr || from == kUnboxedFfiIntPtr));
    SetInputAt(0, value);
  }

Either the asserts should be relaxed (and implemented in ::EmitCode) or the IntConverterInstrs should not me coalesced.

Also, truncation should not get lost.