Closed IbrahimFadel closed 3 years ago
Sorry, i didn't see that document. That answers my question about the struct type, but is there anything I can do about %SomeTypeName
replacing any instance of the i32
type?
minimal replacing example
package main
import (
"fmt"
"github.com/llir/llvm/ir"
"github.com/llir/llvm/ir/constant"
"github.com/llir/llvm/ir/types"
)
func main() {
mod := ir.NewModule()
someTypeName := mod.NewTypeDef("SomeTypeName", types.I32)
fn := mod.NewFunc("main", types.I32)
bb := fn.NewBlock("entry")
bb.NewAlloca(someTypeName)
bb.NewRet(constant.NewInt(types.I32, 0))
fmt.Println(mod)
}
produces:
%SomeTypeName = type i32
define %SomeTypeName @main() {
entry:
%0 = alloca %SomeTypeName
ret %SomeTypeName 0
}
@mewmew I believe this is a bug.
Is the issue that it replaces all i32
types with %SomeTypeName
?
The reason for this is that types.I32 is being defined as %SomeTypeName
in this call:
mod.NewTypeDef("SomeTypeName", types.I32)
Note, types.I32 is a global variable, and ir.Module.NewTypeDef
sets the name of the given type. Thus, all uses of the global variable types.I32
are given the name %SomeTypeName
. This is working as intended, at least by the design of the ir/types
library.
From llir/llvm/ir/module_type.go
:
func (m *Module) NewTypeDef(name string, typ types.Type) types.Type {
typ.SetName(name)
m.TypeDefs = append(m.TypeDefs, typ)
return typ
}
If you want to add a new type def but not replace all instances of i32
, then do as follows (notice the line with types.NewInt(32)
):
package main
import (
"fmt"
"github.com/llir/llvm/ir"
"github.com/llir/llvm/ir/constant"
"github.com/llir/llvm/ir/types"
)
func main() {
mod := ir.NewModule()
someTypeName := mod.NewTypeDef("SomeTypeName", types.NewInt(32)) // <-- notice this line
fn := mod.NewFunc("main", types.I32)
bb := fn.NewBlock("entry")
bb.NewAlloca(someTypeName)
bb.NewRet(constant.NewInt(types.I32, 0))
fmt.Println(mod)
}
Which produces the following LLVM IR:
%SomeTypeName = type i32
define i32 @main() {
entry:
%0 = alloca %SomeTypeName
ret i32 0
}
@IbrahimFadel, on a side note. Glad to see you experimenting with LLVM :)
Curious what you are using type names for. If you are using them just to experiment and test concepts that's great. If you want to use type names to implement functionality which depend on the names, then just wanted to give a quick heads up. In LLVM IR the type name of struct types is used for type identity in type equality checks. That is, two identical structs with different names are considered different types. For all other types, type names are not used for type equality (all other types are checked by structural type equality). That is, the type name of all types except structs are only used as a type name alias, and the LLVM optimizer is free to remove such type names whenever and however it wishes and replace those with their underlying types (and it often does).
Just wanted to let you know this, so you don't start depending on a functionality that the official LLVM compiler will prune away.
WIsh you happy coding adventures and a great summer!
Cheers, Robin
@mewmew Thanks for the working example.
I'm using the type names for typedefs in my toy compiler. So similar to go you can do something like this:
type Foo i32
type Bar struct {
...
}
I just wanted to solve that issue of all instances of i32
being replaced because I want to emit IR that makes sense given the source, so ideally it would preserve the type declaration's name, but only in the places where you actually use that type. But the functionality doesn't depend on it so it's all good.
Again, thanks for your help, and have a great summer yourself!
I'm using the type names for typedefs in my toy compiler.
Cool!
Again, thanks for your help, and have a great summer yourself!
Most happy to help! Best of luck with pi-lang and happy hacking!
Cheers, Robin
@IbrahimFadel if you want pi-lang
to appear on the Users section of the llir/llvm
repo, just send a PR to update the README :) See #187 for an example PR.
Oh wow that would be great! Once i've made some more progress in my go rewrite, and i'll be happy to make a PR :)
Oh wow that would be great! Once i've made some more progress in my go rewrite, and i'll be happy to make a PR :)
Lovely :)
P.S. sent you a tiny PR at IbrahimFadel/pi-lang#8.
I'm trying to creat typedefs and struct types, but ran into some issues. Here is code the replicate the issues:
And this is what the module looks like:
Firstly, when i do
someTypeName := mod.NewTypeDef("SomeTypeName", types.I32)
, I expect that it only uses%SomeTypeName
when I explicitly specify to use it. But instead, whenever it sees ani32
it replaces it with%SomeTypeName
(this is shown byret %SomeTypeName 0
). If this is expected behaviour, I would ask what can I use instead?Secondly, when I create the struct type, I expect it to create a type at the top of the module like:
%Foo = type { i32, i32, i32 }
and then use%Foo
in thealloca
instruction. But there is no type definition forFoo
, it just references it so this won't compile. Again, I don't know if this is expected behaviour or not, how can I get the behaviour I'm looking for?I'm currently porting my C++ code to Go, and with LLVM's C++ bindings I got the behaviour I was expecting automatically, but perhaps I'm using these bindings incorrectly.