reflect: tagged pointers#3691
Conversation
9e4f337 to
fb95822
Compare
|
Note to self: I need to look into why OptimizeReflectImplements fails. |
|
Found so far:
|
|
I think I have a fix. Now I need to properly test it and clean up the code. |
This is necessary to get #3691 working.
|
This should fix most/all errors: #3737 I tested it by applying the 3 patches on top of this branch, and |
This is necessary to get #3691 working.
|
@dgryski can you rebased this PR? My fix has been merged. |
fb95822 to
2a636d6
Compare
|
Rebased. |
|
So my hacked up |
|
So
|
|
Yes, I'm pretty sure they did. Checking the |
|
Ok, more playing around here. The hacked up |
|
The problem with We can see that while the type of |
|
I think this might be an issue with the pass that lowers type asserts. With ptrTo gone, it doesn't know the pointer type can still be created. |
|
I was looking at https://github.com/tinygo-org/tinygo/blob/dev/compiler/interface.go#L661 and wondering if the issue is more related to the fact that we don't handle those pointer types because there aren't named globals for them (but I'm not sure what the fix needs to be...) |
Yes, exactly, that's more or less what I meant. There are no named globals because the One possible fix could be to change the code that lowers type asserts and make sure it also checks whether the element type exists. See the TODO in this code: tinygo/transform/interface-lowering.go Lines 288 to 300 in f366cd5 |
|
Here is a very simple reproducer of the type switch issue: package main
func main() {
identify(0)
identify(new(int))
identify(new(*int))
}
func identify(itf any) {
switch itf.(type) {
case int:
println("type is int")
case *int:
println("type is *int")
case **int:
println("type is **int")
default:
println("other type??")
}
}Go: TinyGo on the dev branch: This branch: So the problem has nothing to do with reflect, just with type asserts (type switches are implemented as an if-else chain made of type asserts). |
|
Here is a fix: diff --git a/compiler/interface.go b/compiler/interface.go
index bd6970b4..d9c0e138 100644
--- a/compiler/interface.go
+++ b/compiler/interface.go
@@ -673,11 +673,8 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
} else {
- assertedTypeGlobal := b.getTypeCode(expr.AssertedType)
- if !assertedTypeGlobal.IsAConstantExpr().IsNil() {
- assertedTypeGlobal = assertedTypeGlobal.Operand(0) // resolve the GEP operation
- }
- globalName := "reflect/types.typeid:" + strings.TrimPrefix(assertedTypeGlobal.Name(), "reflect/types.type:")
+ name, _ := getTypeCodeName(expr.AssertedType)
+ globalName := "reflect/types.typeid:" + name
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
if assertedTypeCodeGlobal.IsNil() {
// Create a new typecode global.
diff --git a/transform/interface-lowering.go b/transform/interface-lowering.go
index ebd47ff8..1cbebee8 100644
--- a/transform/interface-lowering.go
+++ b/transform/interface-lowering.go
@@ -285,11 +285,26 @@ func (p *lowerInterfacesPass) run() error {
for _, use := range getUses(p.mod.NamedFunction("runtime.typeAssert")) {
actualType := use.Operand(0)
name := strings.TrimPrefix(use.Operand(1).Name(), "reflect/types.typeid:")
+ gepOffset := uint64(0)
+ for strings.HasPrefix(name, "pointer:pointer:") {
+ // This is a type like **int, which has the name pointer:pointer:int
+ // but is encoded using pointer tagging.
+ // Calculate the pointer tag, which is emitted as a GEP instruction.
+ name = name[len("pointer:"):]
+ gepOffset++
+ }
if t, ok := p.types[name]; ok {
// The type exists in the program, so lower to a regular pointer
// comparison.
p.builder.SetInsertPointBefore(use)
- commaOk := p.builder.CreateICmp(llvm.IntEQ, t.typecodeGEP, actualType, "typeassert.ok")
+ typecodeGEP := t.typecodeGEP
+ if gepOffset != 0 {
+ // This is a tagged pointer.
+ typecodeGEP = llvm.ConstInBoundsGEP(p.ctx.Int8Type(), typecodeGEP, []llvm.Value{
+ llvm.ConstInt(p.ctx.Int64Type(), gepOffset, false),
+ })
+ }
+ commaOk := p.builder.CreateICmp(llvm.IntEQ, typecodeGEP, actualType, "typeassert.ok")
use.ReplaceAllUsesWith(commaOk)
} else {
// The type does not exist in the program, so lower to a constant |
|
Was that last commit intended to be part of this PR? |
|
Oops, probably not. I'll put that in a separate one. It was included in the original **T branch (since we need it) but yeah it should be its own PR. |
f78bdc8 to
b8b6a5b
Compare
|
@aykevl any other feedback on this PR? The previous change that you suggested has been added. |
aykevl
left a comment
There was a problem hiding this comment.
Did you run the test corpus on this PR? If not, that's something that I would recommend (because this is a rather invasive change).
Other than that, and the (hopefully easy to fix) issue mentioned below, this looks good to me.
| if typ, ok := typ.(*types.Pointer); ok { | ||
| if _, ok := typ.Elem().(*types.Pointer); ok { | ||
| // For a pointer to a pointer, we just increase the pointer by 1 | ||
| ptr := c.getTypeCode(typ.Elem()) | ||
| return llvm.ConstGEP(c.ctx.Int8Type(), ptr, []llvm.Value{ | ||
| llvm.ConstInt(llvm.Int32Type(), 1, false), | ||
| }) | ||
| } | ||
| } |
There was a problem hiding this comment.
I think this should have a check for *****T types, which will overflow the pointer tagging scheme. Better have a compiler error than a mysterious runtime error.
You could for example use .Operand(...) on the GEP expression to check the offset and make sure it's at most 3.
|
I'd love if I could get a position to report the error from instead of just |
Hmm, that's difficult. If it's a pointer to a named type, you could use the location of the named type - but that's still not very useful. I think what is needed is to update all callers to provide a position because the position is not stored in the |
This is necessary to get tinygo-org#3691 working.
This is necessary to get #3691 working.
Doesn't work yet. Still segfaults the compiler.
EDIT by @deadprogram it does work now 😸