Browse Source

compiler: add min and max builtin support

pull/3853/head
Ayke van Laethem 1 year ago
committed by Ron Evans
parent
commit
f791c821ff
  1. 18
      compiler/compiler.go
  2. 3
      compiler/compiler_test.go
  3. 53
      compiler/testdata/go1.21.go
  4. 152
      compiler/testdata/go1.21.ll
  5. 10
      main_test.go
  6. 12
      testdata/go1.21.go
  7. 2
      testdata/go1.21.txt

18
compiler/compiler.go

@ -1637,6 +1637,24 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
llvmLen = b.CreateZExt(llvmLen, b.intType, "len.int")
}
return llvmLen, nil
case "min", "max":
// min and max builtins, added in Go 1.21.
// We can simply reuse the existing binop comparison code, which has all
// the edge cases figured out already.
tok := token.LSS
if callName == "max" {
tok = token.GTR
}
result := argValues[0]
typ := argTypes[0]
for _, arg := range argValues[1:] {
cmp, err := b.createBinOp(tok, typ, typ, result, arg, pos)
if err != nil {
return result, err
}
result = b.CreateSelect(cmp, result, arg, "")
}
return result, nil
case "print", "println":
for i, value := range argValues {
if i >= 1 && callName == "println" {

3
compiler/compiler_test.go

@ -54,6 +54,9 @@ func TestCompiler(t *testing.T) {
if goMinor >= 20 {
tests = append(tests, testCase{"go1.20.go", "", ""})
}
if goMinor >= 21 {
tests = append(tests, testCase{"go1.21.go", "", ""})
}
for _, tc := range tests {
name := tc.file

53
compiler/testdata/go1.21.go

@ -0,0 +1,53 @@
package main
func min1(a int) int {
return min(a)
}
func min2(a, b int) int {
return min(a, b)
}
func min3(a, b, c int) int {
return min(a, b, c)
}
func min4(a, b, c, d int) int {
return min(a, b, c, d)
}
func minUint8(a, b uint8) uint8 {
return min(a, b)
}
func minUnsigned(a, b uint) uint {
return min(a, b)
}
func minFloat32(a, b float32) float32 {
return min(a, b)
}
func minFloat64(a, b float64) float64 {
return min(a, b)
}
func minString(a, b string) string {
return min(a, b)
}
func maxInt(a, b int) int {
return max(a, b)
}
func maxUint(a, b uint) uint {
return max(a, b)
}
func maxFloat32(a, b float32) float32 {
return max(a, b)
}
func maxString(a, b string) string {
return max(a, b)
}

152
compiler/testdata/go1.21.ll

@ -0,0 +1,152 @@
; ModuleID = 'go1.21.go'
source_filename = "go1.21.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
%runtime._string = type { ptr, i32 }
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #2 {
entry:
ret void
}
; Function Attrs: nounwind
define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #2 {
entry:
ret i32 %a
}
; Function Attrs: nounwind
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
entry:
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
ret i32 %0
}
; Function Attrs: nounwind
define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #2 {
entry:
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
ret i32 %1
}
; Function Attrs: nounwind
define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #2 {
entry:
%0 = call i32 @llvm.smin.i32(i32 %a, i32 %b)
%1 = call i32 @llvm.smin.i32(i32 %0, i32 %c)
%2 = call i32 @llvm.smin.i32(i32 %1, i32 %d)
ret i32 %2
}
; Function Attrs: nounwind
define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #2 {
entry:
%0 = call i8 @llvm.umin.i8(i8 %a, i8 %b)
ret i8 %0
}
; Function Attrs: nounwind
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
entry:
%0 = call i32 @llvm.umin.i32(i32 %a, i32 %b)
ret i32 %0
}
; Function Attrs: nounwind
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
entry:
%0 = fcmp olt float %a, %b
%1 = select i1 %0, float %a, float %b
ret float %1
}
; Function Attrs: nounwind
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 {
entry:
%0 = fcmp olt double %a, %b
%1 = select i1 %0, double %a, double %b
ret double %1
}
; Function Attrs: nounwind
define hidden %runtime._string @main.minString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
entry:
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
%stackalloc = alloca i8, align 1
%4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #4
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
%6 = extractvalue %runtime._string %5, 0
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #4
ret %runtime._string %5
}
declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1
; Function Attrs: nounwind
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
entry:
%0 = call i32 @llvm.smax.i32(i32 %a, i32 %b)
ret i32 %0
}
; Function Attrs: nounwind
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
entry:
%0 = call i32 @llvm.umax.i32(i32 %a, i32 %b)
ret i32 %0
}
; Function Attrs: nounwind
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
entry:
%0 = fcmp ogt float %a, %b
%1 = select i1 %0, float %a, float %b
ret float %1
}
; Function Attrs: nounwind
define hidden %runtime._string @main.maxString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
entry:
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
%2 = insertvalue %runtime._string zeroinitializer, ptr %b.data, 0
%3 = insertvalue %runtime._string %2, i32 %b.len, 1
%stackalloc = alloca i8, align 1
%4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #4
%5 = select i1 %4, %runtime._string %1, %runtime._string %3
%6 = extractvalue %runtime._string %5, 0
call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #4
ret %runtime._string %5
}
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
declare i32 @llvm.smin.i32(i32, i32) #3
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
declare i8 @llvm.umin.i8(i8, i8) #3
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
declare i32 @llvm.umin.i32(i32, i32) #3
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
declare i32 @llvm.smax.i32(i32, i32) #3
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
declare i32 @llvm.umax.i32(i32, i32) #3
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { nocallback nofree nosync nounwind readnone speculatable willreturn }
attributes #4 = { nounwind }

10
main_test.go

@ -71,6 +71,16 @@ func TestBuild(t *testing.T) {
"zeroalloc.go",
}
// Go 1.21 made some changes to the language, which we can only test when
// we're actually on Go 1.21.
_, minor, err := goenv.GetGorootVersion()
if err != nil {
t.Fatal("could not get version:", minor)
}
if minor >= 21 {
tests = append(tests, "go1.21.go")
}
if *testTarget != "" {
// This makes it possible to run one specific test (instead of all),
// which is especially useful to quickly check whether some changes

12
testdata/go1.21.go

@ -0,0 +1,12 @@
package main
func main() {
ia := 1
ib := 5
ic := -3
fa := 1.0
fb := 5.0
fc := -3.0
println("min/max:", min(ia, ib, ic), max(ia, ib, ic))
println("min/max:", min(fa, fb, fc), max(fa, fb, fc))
}

2
testdata/go1.21.txt

@ -0,0 +1,2 @@
min/max: -3 5
min/max: -3.000000e+000 +5.000000e+000
Loading…
Cancel
Save