Skip to content

Commit 7460734

Browse files
aykevldeadprogram
authored andcommitted
compiler: mark string parameters as readonly
Strings are readonly, but the compiler doesn't always know this. Marking them as readonly in the frontend allows the compiler to optimize based on this knowledge. This provides some small code size benefits. I didn't measure running speed.
1 parent 3be7100 commit 7460734

File tree

5 files changed

+25
-12
lines changed

5 files changed

+25
-12
lines changed

compiler/calls.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ const (
3232
// Whether this is a full or partial Go parameter (int, slice, etc).
3333
// The extra context parameter is not a Go parameter.
3434
paramIsGoParam = 1 << iota
35+
36+
// Whether this is a readonly parameter (for example, a string pointer).
37+
paramIsReadonly
3538
)
3639

3740
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
@@ -167,6 +170,7 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
167170
continue
168171
}
169172
suffix := strconv.Itoa(i)
173+
isString := false
170174
if goType != nil {
171175
// Try to come up with a good suffix for this struct field,
172176
// depending on which Go type it's based on.
@@ -183,12 +187,16 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
183187
suffix = []string{"r", "i"}[i]
184188
case types.String:
185189
suffix = []string{"data", "len"}[i]
190+
isString = true
186191
}
187192
case *types.Signature:
188193
suffix = []string{"context", "funcptr"}[i]
189194
}
190195
}
191196
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
197+
if isString {
198+
subInfos[0].flags |= paramIsReadonly
199+
}
192200
paramInfos = append(paramInfos, subInfos...)
193201
}
194202
return paramInfos

compiler/symbol.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
142142
nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)
143143
llvmFn.AddAttributeAtIndex(i+1, nocapture)
144144
}
145+
if paramInfo.flags&paramIsReadonly != 0 && paramInfo.llvmType.TypeKind() == llvm.PointerTypeKind {
146+
// Readonly pointer parameters (like strings) benefit from being marked as readonly.
147+
readonly := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)
148+
llvmFn.AddAttributeAtIndex(i+1, readonly)
149+
}
145150
}
146151

147152
// Set a number of function or parameter attributes, depending on the

compiler/testdata/go1.20.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ unsafe.String.throw: ; preds = %entry
5050
declare void @runtime.unsafeSlicePanic(ptr) #1
5151

5252
; Function Attrs: nounwind
53-
define hidden ptr @main.unsafeStringData(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
53+
define hidden ptr @main.unsafeStringData(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
5454
entry:
5555
%stackalloc = alloca i8, align 1
5656
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3

compiler/testdata/go1.21.ll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ entry:
7777
}
7878

7979
; Function Attrs: nounwind
80-
define hidden %runtime._string @main.minString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
80+
define hidden %runtime._string @main.minString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
8181
entry:
8282
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
8383
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
@@ -91,7 +91,7 @@ entry:
9191
ret %runtime._string %5
9292
}
9393

94-
declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1
94+
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
9595

9696
; Function Attrs: nounwind
9797
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
@@ -116,7 +116,7 @@ entry:
116116
}
117117

118118
; Function Attrs: nounwind
119-
define hidden %runtime._string @main.maxString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
119+
define hidden %runtime._string @main.maxString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
120120
entry:
121121
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
122122
%1 = insertvalue %runtime._string %0, i32 %a.len, 1

compiler/testdata/string.ll

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ entry:
3131
}
3232

3333
; Function Attrs: nounwind
34-
define hidden i32 @main.stringLen(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
34+
define hidden i32 @main.stringLen(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
3535
entry:
3636
ret i32 %s.len
3737
}
3838

3939
; Function Attrs: nounwind
40-
define hidden i8 @main.stringIndex(ptr %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #2 {
40+
define hidden i8 @main.stringIndex(ptr readonly %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #2 {
4141
entry:
4242
%.not = icmp ult i32 %index, %s.len
4343
br i1 %.not, label %lookup.next, label %lookup.throw
@@ -55,33 +55,33 @@ lookup.throw: ; preds = %entry
5555
declare void @runtime.lookupPanic(ptr) #1
5656

5757
; Function Attrs: nounwind
58-
define hidden i1 @main.stringCompareEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
58+
define hidden i1 @main.stringCompareEqual(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
5959
entry:
6060
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
6161
ret i1 %0
6262
}
6363

64-
declare i1 @runtime.stringEqual(ptr, i32, ptr, i32, ptr) #1
64+
declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #1
6565

6666
; Function Attrs: nounwind
67-
define hidden i1 @main.stringCompareUnequal(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
67+
define hidden i1 @main.stringCompareUnequal(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
6868
entry:
6969
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
7070
%1 = xor i1 %0, true
7171
ret i1 %1
7272
}
7373

7474
; Function Attrs: nounwind
75-
define hidden i1 @main.stringCompareLarger(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
75+
define hidden i1 @main.stringCompareLarger(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
7676
entry:
7777
%0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #3
7878
ret i1 %0
7979
}
8080

81-
declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1
81+
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1
8282

8383
; Function Attrs: nounwind
84-
define hidden i8 @main.stringLookup(ptr %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #2 {
84+
define hidden i8 @main.stringLookup(ptr readonly %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #2 {
8585
entry:
8686
%0 = zext i8 %x to i32
8787
%.not = icmp ugt i32 %s.len, %0

0 commit comments

Comments
 (0)