Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime should pass layout info to alloc() when possible #4479

Open
dgryski opened this issue Sep 23, 2024 · 4 comments
Open

runtime should pass layout info to alloc() when possible #4479

dgryski opened this issue Sep 23, 2024 · 4 comments

Comments

@dgryski
Copy link
Member

dgryski commented Sep 23, 2024

Right now we only have layout info for allocations created via the compiler. We should make it possible for the runtime to pass layout info for common types at least ([]byte, []string, etc).

@dgryski
Copy link
Member Author

dgryski commented Sep 23, 2024

Tried a quick POC below

diff --git a/src/runtime/string.go b/src/runtime/string.go
index aeefe1d4..ceb5ff13 100644
--- a/src/runtime/string.go
+++ b/src/runtime/string.go
@@ -32,6 +32,12 @@ func stringEqual(x, y string) bool {
        return true
 }

+var (
+       // Constants for use with alloc()
+       gcLayoutZero   uintptr
+       gcLayoutNoPtrs = unsafe.Pointer(&gcLayoutZero)
+)
+
 // Return true iff x < y.
 //
 //go:nobounds
@@ -59,7 +65,7 @@ func stringConcat(x, y _string) _string {
                return x
        } else {
                length := x.length + y.length
-               buf := alloc(length, nil)
+               buf := alloc(length, gcLayoutNoPtrs)
                memcpy(buf, unsafe.Pointer(x.ptr), x.length)
                memcpy(unsafe.Add(buf, x.length), unsafe.Pointer(y.ptr), y.length)
                return _string{ptr: (*byte)(buf), length: length}
@@ -72,7 +78,7 @@ func stringFromBytes(x struct {
        len uintptr
        cap uintptr
 }) _string {
-       buf := alloc(x.len, nil)
+       buf := alloc(x.len, gcLayoutNoPtrs)
        memcpy(buf, unsafe.Pointer(x.ptr), x.len)
        return _string{ptr: (*byte)(buf), length: x.len}
 }
@@ -83,7 +89,7 @@ func stringToBytes(x _string) (slice struct {
        len uintptr
        cap uintptr
 }) {
-       buf := alloc(x.length, nil)
+       buf := alloc(x.length, gcLayoutNoPtrs)
        memcpy(buf, unsafe.Pointer(x.ptr), x.length)
        slice.ptr = (*byte)(buf)
        slice.len = x.length
@@ -100,7 +106,7 @@ func stringFromRunes(runeSlice []rune) (s _string) {
        }

        // Allocate memory for the string.
-       s.ptr = (*byte)(alloc(s.length, nil))
+       s.ptr = (*byte)(alloc(s.length, gcLayoutNoPtrs))

        // Encode runes to UTF-8 and store the resulting bytes in the string.
        index := uintptr(0)

but got an interp panic:

panic: interface conversion: interp.value is interp.literalValue, not interp.rawValue

goroutine 126 [running]:
github.com/tinygo-org/tinygo/interp.(*runner).readObjectLayout(0xc004032000, {0x1099fd7e8, 0xc004c24ee0})
	/Users/dgryski/go/src/github.com/tinygo-org/tinygo/interp/memory.go:1295 +0x425
github.com/tinygo-org/tinygo/interp.(*runner).getLLVMTypeFromLayout(0xc004032000, {0x1099fd7e8, 0xc004c24ee0})
	/Users/dgryski/go/src/github.com/tinygo-org/tinygo/interp/memory.go:1314 +0x37
github.com/tinygo-org/tinygo/interp.(*runner).run(0xc004032000, 0xc003a28870, {0xc0011a3a90, 0x3, 0x0?}, 0xc00102cab0, {0xc004c24e78, 0x8})
	/Users/dgryski/go/src/github.com/tinygo-org/tinygo/interp/interpreter.go:288 +0x3899
github.com/tinygo-org/tinygo/interp.(*runner).run(0xc004032000, 0xc003a28820, {0x0, 0x0, 0x0?}, 0x0, {0x109895f4e, 0x4})
	/Users/dgryski/go/src/github.com/tinygo-org/tinygo/interp/interpreter.go:552 +0x821e

Seems like we'll either need to get the compiler to inject the pointer to the appropriate type layout, or figure out a way to get access to the existing symbol. I'm concerned that fixing interp will actually move more of those string computations away from the interp phase and into runtime, which is sadly backwards from what we want.

@dgryski
Copy link
Member Author

dgryski commented Sep 24, 2024

Other requirements: we probably also need the ability to get a layout ptr given a reflect type, and possibly at compile time too (using some sort of compiler lowering pass from a fake method call).

@dgryski
Copy link
Member Author

dgryski commented Sep 27, 2024

Realized where I went wrong here; I should be able to create constant layouts for []byte and string and pass those around; pointers only needed if the allocation doesn't fit in a word.

@aykevl
Copy link
Member

aykevl commented Sep 27, 2024

Correct, you can also look at what the compiler generates in an equivalent case:

%makeslice = call align 1 dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3

So for stringConcat for example (which allocates pointer-free memory) you can use something like unsafe.Pointer(uintptr(0x3)).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants