You'll find that x1() and x2() are different. Then I run `go tool compile -m -m`:
unsafestr.go:11:11: make([]byte, 12) escapes to heap:
unsafestr.go:11:11: flow: b = &{storage for make([]byte, 12)}:
unsafestr.go:11:11: from make([]byte, 12) (spill) at unsafestr.go:11:11
unsafestr.go:11:11: from b := make([]byte, 12) (assign) at unsafestr.go:11:4
unsafestr.go:11:11: flow: pbytes = &b:
unsafestr.go:11:11: from &b (address-of) at unsafestr.go:14:50
unsafestr.go:11:11: from pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) (assign) at unsafestr.go:14:9
unsafestr.go:11:11: flow: {heap} = *pbytes:
unsafestr.go:11:11: from pbytes.Data (reflect.Header.Data) at unsafestr.go:16:23
unsafestr.go:11:11: from pstring.Data = pbytes.Data (assign) at unsafestr.go:16:15
unsafestr.go:11:11: make([]byte, 12) escapes to heap
unsafestr.go:22:11: make([]byte, 12) does not escape
What's the difference between x1 and x2? Both in x1 and x2, b's data pointers are assigned to an uintptr(reflect.StringHeader::Data).