compiler: pass large aggregates by pointer

LLVM ComputeValueVTs recursively expands arrays and structs into one
value type per scalar leaf. SelectionDAG call lowering allocates data
structures proportional to this count, which makes very large values
exhaust memory or crash LLVM.

Count scalar leaves and use pointers for internal parameters and results
when the count exceeds 1024. A result pointer is the first parameter,
and aggregate parameters point to read-only memory. Exported function
types are unchanged.

Keep these SSA values in memory and copy them with memcpy when needed.
Handle calls, interfaces, maps, channels, selects, defers, goroutines,
phis, and multiple results. Update the expected compiler IR and re-enable
the native compress/flate tests.
This commit is contained in:
Jake Bailey
2026-07-14 06:22:02 -07:00
committed by Damian Gryski
parent 3b7c9e24f5
commit 9e7d89d4d5
14 changed files with 793 additions and 234 deletions
+9 -9
View File
@@ -100,14 +100,14 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m llvm.Value, k
} else {
// Key stored at actual type: either binary-comparable or with
// compiler-generated hash/equal.
mapKeyAlloca, mapKeySize := b.getValueStorage(key, "hashmap.key")
params := []llvm.Value{m, mapKeyAlloca, mapValueAlloca, mapValueSize}
mapKey := b.getValueStorage(key, "hashmap.key")
params := []llvm.Value{m, mapKey.ptr, mapValueAlloca, mapValueSize}
fnName := "hashmapBinaryGet"
if !hashmapIsBinaryKey(keyType) {
fnName = "hashmapGenericGet"
}
commaOkValue = b.createRuntimeCall(fnName, params, "")
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize)
b.endValueStorage(mapKey)
}
// The value is set to the zero value if the key doesn't exist.
@@ -117,24 +117,24 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m llvm.Value, k
// createMapUpdate updates a map key to a given value, by creating an
// appropriate runtime call.
func (b *builder) createMapUpdate(keyType types.Type, m llvm.Value, key, value ssa.Value, pos token.Pos) {
valueAlloca, valueSize := b.getValueStorage(value, "hashmap.value")
storedValue := b.getValueStorage(value, "hashmap.value")
keyType = keyType.Underlying()
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
// key is a string
params := []llvm.Value{m, b.getValue(key, getPos(key)), valueAlloca}
params := []llvm.Value{m, b.getValue(key, getPos(key)), storedValue.ptr}
b.createRuntimeInvoke("hashmapStringSet", params, "")
} else {
// Key stored at actual type.
keyAlloca, keySize := b.getValueStorage(key, "hashmap.key")
keyStorage := b.getValueStorage(key, "hashmap.key")
fnName := "hashmapBinarySet"
if !hashmapIsBinaryKey(keyType) {
fnName = "hashmapGenericSet"
}
params := []llvm.Value{m, keyAlloca, valueAlloca}
params := []llvm.Value{m, keyStorage.ptr, storedValue.ptr}
b.createRuntimeInvoke(fnName, params, "")
b.emitLifetimeEnd(keyAlloca, keySize)
b.endValueStorage(keyStorage)
}
b.emitLifetimeEnd(valueAlloca, valueSize)
b.endValueStorage(storedValue)
}
// createMapDelete deletes a key from a map by calling the appropriate runtime