OptimizeStringToBytes transforms runtime.stringToBytes(...) calls into const []byte slices whenever possible. This optimizes the following pattern: w.Write([]byte("foo")) where Write does not store to the slice.
(mod llvm.Module)
| 14 | // |
| 15 | // where Write does not store to the slice. |
| 16 | func OptimizeStringToBytes(mod llvm.Module) { |
| 17 | stringToBytes := mod.NamedFunction("runtime.stringToBytes") |
| 18 | if stringToBytes.IsNil() { |
| 19 | // nothing to optimize |
| 20 | return |
| 21 | } |
| 22 | |
| 23 | for _, call := range getUses(stringToBytes) { |
| 24 | strptr := call.Operand(0) |
| 25 | strlen := call.Operand(1) |
| 26 | |
| 27 | // strptr is always constant because strings are always constant. |
| 28 | |
| 29 | var pointerUses []llvm.Value |
| 30 | canConvertPointer := true |
| 31 | for _, use := range getUses(call) { |
| 32 | if use.IsAExtractValueInst().IsNil() { |
| 33 | // Expected an extractvalue, but this is something else. |
| 34 | canConvertPointer = false |
| 35 | break |
| 36 | } |
| 37 | switch use.Type().TypeKind() { |
| 38 | case llvm.IntegerTypeKind: |
| 39 | // A length (len or cap). Propagate the length value. |
| 40 | // This can always be done because the byte slice is always the |
| 41 | // same length as the original string. |
| 42 | use.ReplaceAllUsesWith(strlen) |
| 43 | use.EraseFromParentAsInstruction() |
| 44 | case llvm.PointerTypeKind: |
| 45 | // The string pointer itself. |
| 46 | if !isReadOnly(use) { |
| 47 | // There is a store to the byte slice. This means that none |
| 48 | // of the pointer uses can't be propagated. |
| 49 | canConvertPointer = false |
| 50 | break |
| 51 | } |
| 52 | // It may be that the pointer value can be propagated, if all of |
| 53 | // the pointer uses are readonly. |
| 54 | pointerUses = append(pointerUses, use) |
| 55 | default: |
| 56 | // should not happen |
| 57 | panic("unknown return type of runtime.stringToBytes: " + use.Type().String()) |
| 58 | } |
| 59 | } |
| 60 | if canConvertPointer { |
| 61 | // All pointer uses are readonly, so they can be converted. |
| 62 | for _, use := range pointerUses { |
| 63 | use.ReplaceAllUsesWith(strptr) |
| 64 | use.EraseFromParentAsInstruction() |
| 65 | } |
| 66 | |
| 67 | // Call to runtime.stringToBytes can be eliminated: both the input |
| 68 | // and the output is constant. |
| 69 | call.EraseFromParentAsInstruction() |
| 70 | } |
| 71 | } |
| 72 | } |
| 73 |