Repository: go
Issue: gollvm: cannot use unsafe.Pointer as map key type
Labels: NeedsFix

GOLLVM has a miscompilation bug that can be reproduced with the following test case.

The bug can be reproduced with trunk:

1. gollvm: https://go.googlesource.com/gollvm/+/0e34e09fc15cde73f1b9974f2a657360abb94b4f
2. gofrontend: https://go.googlesource.com/gofrontend/+/6a33e7e30c89edc12340dc470b44791bb1066feb

In the following test case, `m` is a map with `unsafe.Pointer` as key type. Method `test` simply adds key `p` into map `m` and returns true if the key is not present in the map. So `ok1` should be `true` and `ok2` should be `false`. The `gc` compiler can produce the correct output whereas GOLLVM miscompiles and leads to wrong output, i.e., both `ok1` and `ok2` are true.

```go
package main

import (
	"unsafe"
)

func test(m map[unsafe.Pointer]struct{}, p unsafe.Pointer) bool {
	if _, ok := m[p]; ok {
		return false
	}
	m[p] = struct{}{}
	return true
}

func main() {
	m := make(map[unsafe.Pointer]struct{})
	p := unsafe.Pointer(&m)
	ok1 := test(m, p)
	ok2 := test(m, p)
	if !ok1 || ok2 {
		panic("Oops")
	}
}
```

Below is the LLVM IR obtained via option `-tracelevel=1`.

```llvm
define internal i8 @main.test(i8* nest %nest.0, { i64, i8, i8, i16, i32, i8*, i8*, i64, i8* }* %m, i8* %p) #0 !dbg !5 {
entry:
  %m.addr = alloca { i64, i8, i8, i16, i32, i8*, i8*, i64, i8* }*, align 8
  %p.addr = alloca i8*, align 8
  %"$ret0" = alloca i8, align 1
  %ok = alloca i8, align 1
  %tmpv.0 = alloca i8*, align 8, !go_addrtaken !31
  %tmpv.1 = alloca {}*, align 8
  %tmpv.2 = alloca i8, align 1
  %tmpv.3 = alloca { i8*, i8 }, align 8
  %sret.actual.0 = alloca { i8*, i8 }, align 8
  %tmpv.4 = alloca i8*, align 8
  %tmpv.5 = alloca i8, align 1
  %tmpv.6 = alloca i8*, align 8, !go_addrtaken !31
  %tmpv.7 = alloca {}, align 1
  %tmpv.8 = alloca i8*, align 8
  store { i64, i8, i8, i16, i32, i8*, i8*, i64, i8* }* %m, { i64, i8, i8, i16, i32, i8*, i8*, i64, i8* }** %m.addr, align 8
  call void @llvm.dbg.declare(metadata { i64, i8, i8, i16, i32, i8*, i8*, i64, i8* }** %m.addr, metadata !32, metadata !DIExpression()), !dbg !33
  store i8* %p, i8** %p.addr, align 8
  call void @llvm.dbg.declare(metadata i8** %p.addr, metadata !34, metadata !DIExpression()), !dbg !35
  call void @llvm.lifetime.start.p0i8(i64 1, i8* %"$ret0")
  store i8 0, i8* %"$ret0", align 1
  call void @llvm.dbg.declare(metadata i8* %"$ret0", metadata !36, metadata !DIExpression()), !dbg !37
  call void @llvm.lifetime.start.p0i8(i64 1, i8* %ok)
  store i8 0, i8* %ok, align 1
  call void @llvm.dbg.declare(metadata i8* %ok, metadata !38, metadata !DIExpression()), !dbg !40
  %p.ld.0 = load i8*, i8** %p.addr, align 8, !dbg !41
  store i8* %p.ld.0, i8** %tmpv.0, align 8
  %m.ld.0 = load { i64, i8, i8, i16, i32, i8*, i8*, i64, i8* }*, { i64, i8, i8, i16, i32, i8*, i8*, i64, i8* }** %m.addr, align 8, !dbg !42
  %tmpv.0.ld.0 = load i8*, i8** %tmpv.0, align 8, !dbg !43 # <----- additional load
  %cast.22 = bitcast i8* %tmpv.0.ld.0 to i64*, !dbg !43 # <----- following cast
  %deref.ld.0 = load i64, i64* %cast.22, align 8, !dbg !43
  %call.0 = call { i8*, i8 } @runtime.mapaccess2__fast64(i8* nest undef, %_type.0* getelementptr inbounds (%MapType.0, %MapType.0* @type..map_6unsafe_0Pointer_7struct_4_5, i32 0, i32 0), { i64, i8, i8, i16, i32, i8*, i8*, i64, i8* }* %m.ld.0, i64 %deref.ld.0), !dbg !43
...
```


We can find that there is an additional load that converts `i8**` into `i8*` and then `i8*` is converted into `i64*`,
which means not the address of `p` (i.e., type `i8**`) is converted into `i64*` but the `p` (i.e., type `i8*`) itself is converted into `i64*` (). 



It seems that there is something wrong with the propagation of `VarContext`. Additional load operations are generated during materialization.