More strangeness: ReadOnlyMemoryError() in 0.6.2 (longish post)


#1

Earlier I reported a mysterious ReadOnlyMemoryError() in 0.6.2 under linux only. Someone else recently posted another problem with ReadOnlyMemoryError(), I decided to dig a bit deeper into my example, and the Julia behavior I uncovered is mysterious.

My code is in five modules, each of which is one file, say A.jl, B.jl, C.jl, D.jl, E.jl. Both D and E have using statements referring to A, B, C. So I should include them in the order A, B, C, D, E from the Repl. I discovered that if I include D first, then of course I get an error immediately about an undefined module. But after this error, if I then include them in the right order, the ReadOnlyMemory() goes away! In other words, if I include them in the correct order A, B, C, D, E, then the code fails with ReadOnlyMemoryError, but if I include them in the order D, A, B, C, D, E, then it works. (The first inclusion of D yields an immediate error.)

So I decided to check whether different assembly is being emitted for my code in the two scenarios (two different include orders) . The code_lowered’s are the same, but the LLVMs are different. Most of the differences appear to be variation in lengthy integers, which I presume are absolute memory addresses. However, there are some differences in the order of statements as well. I included one such difference below. Can someone who understands LLVM explain this? Note: I am not claiming that the LLVM below actually emits the ReadOnlyMemoryError; instead, I am just showing that the same code unexpectedly compiled into two different LLVMs.

This is the scenario when I included A, B, C, D, E:

L282:                                             ; preds = %L275
  %619 = load i64, i64* inttoptr (i64 139879721017720 to i64*), align 8
  store i64 %619, i64* %88, align 8
  store i8** inttoptr (i64 139879659292584 to i8**), i8*** %79, align 8
  %620 = call i8** @jl_f_getfield(i8** null, i8*** %37, i32 2)
  store i8** %620, i8*** %38, align 8
  store i8** inttoptr (i64 139879659297368 to i8**), i8*** %77, align 8
  %621 = call i8** @jl_f_getfield(i8** null, i8*** %38, i32 2)
  store i8** %621, i8*** %19, align 8
  store i8** inttoptr (i64 139874949373776 to i8**), i8*** %78, align 8
  %622 = call i8** @jl_apply_generic(i8*** %19, i32 2)
  store i8** %622, i8*** %356, align 8
  call void @jl_throw(i8** %622)
  unreachable

This is the scenario when I included D, A, B, C, D, E:

  %619 = load i64, i64* inttoptr (i64 139989567349112 to i64*), align 8
  %620 = bitcast i8*** %38 to i64*
  store i64 %619, i64* %620, align 8
  store i8** inttoptr (i64 139989505628072 to i8**), i8*** %77, align 8
  %621 = call i8** @jl_f_getfield(i8** null, i8*** %38, i32 2)
  store i8** %621, i8*** %37, align 8
  store i8** inttoptr (i64 139989505632856 to i8**), i8*** %79, align 8
  %622 = call i8** @jl_f_getfield(i8** null, i8*** %37, i32 2)
  store i8** %622, i8*** %19, align 8
  store i8** inttoptr (i64 139984803236368 to i8**), i8*** %78, align 8
  %623 = call i8** @jl_apply_generic(i8*** %19, i32 2)
  store i8** %623, i8*** %356, align 8
  call void @jl_throw(i8** %623)
  unreachable