Kite Programming Language

Interesting LLVM "bug" found

Written by Mooneer Salem on Monday 16th of January, 2012 in General

Today I added parser and codegen support for the return keyword. As in other languages, its designed for the developer to exit a method early. Unfortunately, it didnt work right away:

$ ./kite
method x()
[
    return 1;
];

x()|print;
^D
System.exceptions.NotImplemented: Could not find method print that takes 0 argument(s).
    in (main program) + 0x79
$

Investigating further, I noticed that it was generating the following LLVM intermediate code:

; ModuleID = '__root_module'

@0 = internal constant [5 x i8] c"x__o\00"
@1 = internal constant [6 x i8] c"print\00"

define i32* @__static_init____o(i32* %this) {
entry:
  %0 = alloca i32*
  store i32* %this, i32** %0
  %1 = call i32* @kite_method_alloc(i32* bitcast (i32* (i32*)* @x__o to i32*), i32 1)
  %2 = load i32** %0
  %3 = call i32** @kite_dynamic_object_get_property(i32* %2, i8* getelementptr inbounds ([5 x i8]* @0, i32 0, i32 0), i1 true)
  store i32* %1, i32** %3
  %4 = load i32** %0
  %5 = call i32* @x__o(i32* %4)
  %6 = call i32* @kite_find_funccall(i32* %5, i8* getelementptr inbounds ([6 x i8]* @1, i32 0, i32 0), i32 1)
  %7 = bitcast i32* %6 to i32* (i32*)*
  %8 = call i32* %7(i32* %5)
  ret i32* %8
}

define i32* @x__o(i32* %this) {
entry:
  %0 = alloca i32*
  store i32* %this, i32** %0
  %1 = call i32* @System__integer__obj__i(i32 1)
  ret i32* %1
  ret i32* %1
}

declare i32* @System__integer__obj__i(i32)

declare i32* @kite_method_alloc(i32*, i32)

declare i32** @kite_dynamic_object_get_property(i32*, i8*, i1)

declare i32* @kite_find_funccall(i32*, i8*, i32)

Note the part in bold. Two ret statements are being outputthe one from return, and the normal one thats generated at the end of every method. Normally LLVM would eventually generate machine code that simply skips over the second ret statement. Or so youd think.

What I noticed is that System__integer__obj__i() would do the correct thing and return a valid System::object pointer, while kite_find_funccall() would get a totally different pointer. Even though according to the above, the return value from obj__i() gets fed directly into kite_find_funccall(). I confirmed this time and time again in gdb.

The fix? Using the same code suppression logic that I implemented for break/continue to suppress the last ret. Now it works as intended:

$ ./kite
method x()
[
    return 1;
];

x()|print;
^D
1
$

EDIT: this was on LLVM 2.9. I have yet to try 3.0 to see if this issues fixed there. If not, a bug report might be pending in the future.

EDIT 2: Link to LLVM bug report. Was able to duplicate the problem in 3.0 as well.

Add comment