ponylang/ponyc

Compile fails verification "GEP base pointer is not a vector or a vector of pointers"

Closed this issue · 13 comments

Full error is:

Verifying
GEP base pointer is not a vector or a vector of pointers
  %7 = getelementptr inbounds %39, %39 %6, i64 0, i32 4
Error:
Module verification failed: GEP base pointer is not a vector or a vector of pointers
  %7 = getelementptr inbounds %39, %39 %6, i64 0, i32 4

    Info:
Please file an issue ticket. Use --noverify to bypass this error.

Ubuntu, ponyc version:

0.58.0-a161b7c [release]
Compiled with: LLVM 15.0.7 -- Clang-14.0.0-x86_64
Defaults: pic=true

Minimal Example (https://playground.ponylang.io/?gist=693d41462cb1b63f03eb8ae1a71f14c7)

type CBA is @{(): None}

actor Main
  let maintag: Main tag = recover tag this end
  new create(env: Env) =>
    let act: MyActor[Main tag] = MyActor[Main tag]

actor MyActor[A: Any tag]
  let cb: Callbacks[A] = Callbacks[A]


class Callbacks[A: Any tag]
  var cbi: CBA = @{() => None}

More minimal:

actor Main
  new create(env: Env) => None
    let cb: Callbacks = Callbacks

class Callbacks
  var cbi: @{(): None}
  
  new create() =>
    cbi = @{() => None}

If I change from bare functions to pony lambdas, it compiles and verifies.

actor Main
  new create(env: Env) => None
    let cb: Callbacks = Callbacks

class Callbacks
  var cbi: {(): None}
  
  new create() =>
    cbi = {() => None}

If I run this with the --noverify option I get this:

red@adipose:~/projects/minimal$ ponyc --noverify
Building builtin -> /home/red/.local/share/ponyup/ponyc-release-0.58.0-x86_64-linux-ubuntu22.04/packages/builtin
Building . -> /home/red/projects/minimal
Generating
 Reachability
 Selector painting
 Data prototypes
 Data types
 Function prototypes
 Functions
 Descriptors
Writing ./minimal.o
LLVM fatal error: Cannot emit physreg copy instruction
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.      Running pass 'Function Pass Manager' on module 'minimal'.
1.      Running pass 'Post-RA pseudo instruction expansion pass' on function '@Callbacks_Deserialise'
Aborted (core dumped)

Backtrace:

red@adipose:~/projects/minimal$ lldb -- ponyc --noverify .
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ModuleNotFoundError: No module named 'lldb.embedded_interpreter'
(lldb) target create "ponyc"
Current executable set to 'ponyc' (x86_64).
(lldb) settings set -- target.run-args  "--noverify" "."
(lldb) run
Process 10669 launched: '/home/red/.local/share/ponyup/bin/ponyc' (x86_64)
Building builtin -> /home/red/.local/share/ponyup/ponyc-release-0.58.0-x86_64-linux-ubuntu22.04/packages/builtin
Building . -> /home/red/projects/minimal
Generating
 Reachability
 Selector painting
 Data prototypes
 Data types
 Function prototypes
 Functions
 Descriptors
Writing ./minimal.o
LLVM fatal error: Cannot emit physreg copy instruction
Process 10669 stopped
* thread #1, name = 'ponyc', stop reason = signal SIGABRT
    frame #0: 0x00007ffff7c969fc libc.so.6`__GI___pthread_kill at pthread_kill.c:44:76
(lldb) bt
* thread #1, name = 'ponyc', stop reason = signal SIGABRT
  * frame #0: 0x00007ffff7c969fc libc.so.6`__GI___pthread_kill at pthread_kill.c:44:76
    frame #1: 0x00007ffff7c969b0 libc.so.6`__GI___pthread_kill [inlined] __pthread_kill_internal(signo=6, threadid=140737352661696) at pthread_kill.c:78:10
    frame #2: 0x00007ffff7c969b0 libc.so.6`__GI___pthread_kill(threadid=140737352661696, signo=6) at pthread_kill.c:89:10
    frame #3: 0x00007ffff7c42476 libc.so.6`__GI_raise(sig=6) at raise.c:26:13
    frame #4: 0x00007ffff7c287f3 libc.so.6`__GI_abort at abort.c:79:7
    frame #5: 0x0000555557ba3c3c ponyc`llvm::report_fatal_error(llvm::Twine const&, bool) + 460
    frame #6: 0x0000555557ba3a66 ponyc`llvm::report_fatal_error(char const*, bool) + 38
    frame #7: 0x0000555556454fe8 ponyc`llvm::X86InstrInfo::copyPhysReg(llvm::MachineBasicBlock&, llvm::MachineInstrBundleIterator<llvm::MachineInstr, false>, llvm::DebugLoc const&, llvm::MCRegister, llvm::MCRegister, bool) const + 2488
    frame #8: 0x0000555556a4410a ponyc`(anonymous namespace)::ExpandPostRA::runOnMachineFunction(llvm::MachineFunction&) + 538
    frame #9: 0x0000555556b24e5d ponyc`llvm::MachineFunctionPass::runOnFunction(llvm::Function&) + 493
    frame #10: 0x0000555555d455fc ponyc`llvm::FPPassManager::runOnFunction(llvm::Function&) + 876
    frame #11: 0x0000555555d4c853 ponyc`llvm::FPPassManager::runOnModule(llvm::Module&) + 51
    frame #12: 0x0000555555d4617f ponyc`llvm::legacy::PassManagerImpl::run(llvm::Module&) + 2335
    frame #13: 0x0000555556de1749 ponyc`LLVMTargetMachineEmit(LLVMOpaqueTargetMachine*, LLVMOpaqueModule*, llvm::raw_pwrite_stream&, LLVMCodeGenFileType, char**) + 409
    frame #14: 0x0000555556de157c ponyc`LLVMTargetMachineEmitToFile + 172
    frame #15: 0x0000555555bd9899 ponyc`genobj + 345
    frame #16: 0x0000555555bd4cba ponyc`genexe + 570
    frame #17: 0x0000555555bcceef ponyc`codegen + 143
    frame #18: 0x0000555555bc62b4 ponyc`main + 884
    frame #19: 0x00007ffff7c29d90 libc.so.6`__libc_start_call_main(main=(ponyc`main), argc=3, argv=0x00007fffffffe048) at libc_start_call_main.h:58:16
    frame #20: 0x00007ffff7c29e40 libc.so.6`__libc_start_main_impl(main=(ponyc`main), argc=3, argv=0x00007fffffffe048, init=0x00007ffff7ffd040, fini=<unavailable>, rtld_fini=<unavailable>, stack_end=0x00007fffffffe038) at libc-start.c:392:3
    frame #21: 0x0000555555bc5e75 ponyc`_start + 37
(lldb) 

The code in verifier that is going "nope"...

void Verifier::visitGetElementPtrInst(GetElementPtrInst &GEP) {
  Type *TargetTy = GEP.getPointerOperandType()->getScalarType();

  Check(isa<PointerType>(TargetTy),
        "GEP base pointer is not a vector or a vector of pointers", &GEP);
  Check(GEP.getSourceElementType()->isSized(), "GEP into unsized type!", &GEP);

Here's the bad LLVM for when I compile with 0.58.0 locally in release mode:

; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn
define dso_local void @Callbacks_Deserialise(ptr nocapture readnone %0, ptr nocapture %1) unnamed_addr #9 !pony.abi !2 {
  store ptr @10, ptr %1, align 8
  %3 = getelementptr inbounds %36, ptr %1, i64 0, i32 1
  %4 = load i64, ptr %3, align 8
  %5 = getelementptr inbounds [0 x ptr], ptr @21, i64 0, i64 %4
  %6 = load %35, ptr %5, align 8
  %7 = getelementptr inbounds %35, %35 %6, i64 0, i32 4
  %8 = load ptr, ptr addrspace(1) %7, align 8
  store ptr %8, ptr %3, align 8
  ret void
}

%7 is what goes "kaboom!'

Deserialize in the working code:

define dso_local void @Callbacks_Deserialise(ptr %0, ptr nocapture %1) unnamed_addr !pony.abi !2 {
  store ptr @10, ptr %1, align 8
  %3 = getelementptr inbounds %30, ptr %1, i64 0, i32 1
  %4 = load i64, ptr %3, align 8
  %5 = tail call ptr @pony_deserialise_offset(ptr %0, ptr null, i64 %4)
  store ptr %5, ptr %3, align 8
  ret void
}

@jemc I'm not very familiar with the lambda code. Do you know the difference between TK_BARELAMBDATYPE and TK_BARELAMBDA?

Also, any insight into this?

I'm going through to try and find "bare vs normal" lambda differences.

A big one in lambda.c:

  if(bare)
  {
    BUILD(bare_annotation, *astp,
      NODE(TK_ANNOTATION,
        ID("ponyint_bare")));

    // Record the syntax pass as done to avoid the error about internal
    // annotations.
    ast_pass_record(bare_annotation, PASS_SYNTAX);
    ast_setannotation(*astp, bare_annotation);
  }

If we don't set the annotation, we do additional catch up and but don't do syntax (which avoids a compile error that is non-sensical). I'm not sure what else might end up different because of the syntax pass being marked as done.

Removing the call to deserialise_bare_interface for type TK_INTERFACE in gendeserialise_element in genserialise.c gets rid of the issue so, it is something in deserialise_bare_interface is the problem.

The issue was introduced in 0.54.1 when we switched to LLVM 15.

Code prior:

static void deserialise_bare_interface(compile_t* c, LLVMValueRef ptr)
{
  LLVMValueRef type_id = LLVMBuildLoad_P(c->builder, ptr, "");

  LLVMValueRef args[2];
  args[0] = LLVMConstInt(c->i32, 0, false);
  args[1] = LLVMBuildPtrToInt(c->builder, type_id, c->intptr, "");

  LLVMValueRef desc = LLVMBuildInBoundsGEP_P(c->builder, c->desc_table, args, 2,
    "");
  desc = LLVMBuildLoad_P(c->builder, desc, "");
  LLVMValueRef func = gendesc_instance(c, desc);
  LLVMBuildStore(c->builder, func, ptr);
}

Code after:

static void deserialise_bare_interface(compile_t* c, LLVMValueRef ptr)
{
  LLVMValueRef args[2];
  args[0] = LLVMConstInt(c->i32, 0, false);
  args[1] = LLVMBuildLoad2(c->builder, c->intptr, ptr, "");

  LLVMValueRef desc = LLVMBuildInBoundsGEP2(c->builder,
    LLVMArrayType(c->ptr, 0), c->desc_table, args, 2, "");
  desc = LLVMBuildLoad2(c->builder, c->descriptor_type, desc, "");
  LLVMValueRef func = gendesc_instance(c, desc);
  LLVMBuildStore(c->builder, func, ptr);
}

@jemc you touched that code last, any ideas?

jemc commented

Try this:

static void deserialise_bare_interface(compile_t* c, LLVMValueRef ptr)
{
  LLVMValueRef args[2];
  args[0] = LLVMConstInt(c->i32, 0, false);
  args[1] = LLVMBuildLoad2(c->builder, c->intptr, ptr, "");

  LLVMValueRef desc = LLVMBuildInBoundsGEP2(c->builder,
    LLVMArrayType(c->ptr, 0), c->desc_table, args, 2, "");
  desc = LLVMBuildLoad2(c->builder, c->ptr, desc, "");
  LLVMValueRef func = gendesc_instance(c, desc);
  LLVMBuildStore(c->builder, func, ptr);
}

That's the fix!