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

ssa: add support for min and max built-in functions #614

Merged
merged 1 commit into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions ssa/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@
token.NEQ - predOpBase: llvm.IntNE,
}

// EQL NEQ LSS LEQ GTR GEQ == != < <= < >=
// EQL NEQ LSS LEQ GTR GEQ == != < <= > >=
func isPredOp(op token.Token) bool {
return op >= predOpBase && op <= predOpLast
}
Expand All @@ -392,7 +392,7 @@
// op can be:
// ADD SUB MUL QUO REM + - * / %
// AND OR XOR SHL SHR AND_NOT & | ^ << >> &^
// EQL NEQ LSS LEQ GTR GEQ == != < <= < >=
// EQL NEQ LSS LEQ GTR GEQ == != < <= > >=
func (b Builder) BinOp(op token.Token, x, y Expr) Expr {
if debugInstr {
log.Printf("BinOp %d, %v, %v\n", op, x.impl, y.impl)
Expand Down Expand Up @@ -490,7 +490,7 @@
llop := logicOpToLLVM[op-logicOpBase]
return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type}
}
case isPredOp(op): // op: == != < <= < >=
case isPredOp(op): // op: == != < <= > >=
prog := b.Prog
tret := prog.Bool()
kind := x.kind
Expand Down Expand Up @@ -1014,6 +1014,22 @@
return
}

// compareSelect performs a series of comparisons and selections based on the
// given comparison op. It's used to implement operations like min and max.
//
// The function iterates through the provided expressions, comparing each with
// the current result using the specified comparison op. It selects the
// appropriate value based on the comparison.
func (b Builder) compareSelect(op token.Token, x Expr, y ...Expr) Expr {
ret := x
for _, v := range y {
cond := b.BinOp(op, ret, v)
sel := llvm.CreateSelect(b.impl, cond.impl, ret.impl, v.impl)
ret = Expr{sel, ret.Type}
}
return ret
}

// A Builtin represents a specific use of a built-in function, e.g. len.
//
// Builtins are immutable values. Builtins do not have addresses.
Expand Down Expand Up @@ -1124,6 +1140,14 @@
b.Call(b.Pkg.rtFunc("MapClear"), t, m)
return
}
case "min":
if len(args) > 0 {
return b.compareSelect(token.LSS, args[0], args[1:]...)

Check warning on line 1145 in ssa/expr.go

View check run for this annotation

Codecov / codecov/patch

ssa/expr.go#L1143-L1145

Added lines #L1143 - L1145 were not covered by tests
}
case "max":
if len(args) > 0 {
return b.compareSelect(token.GTR, args[0], args[1:]...)

Check warning on line 1149 in ssa/expr.go

View check run for this annotation

Codecov / codecov/patch

ssa/expr.go#L1147-L1149

Added lines #L1147 - L1149 were not covered by tests
}
}
panic("todo: " + fn)
}
Expand Down
31 changes: 31 additions & 0 deletions ssa/ssa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,3 +521,34 @@ func TestBasicType(t *testing.T) {
}
}
}

func TestCompareSelect(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已经很少这样写测试案例了。推荐在 _testdata 目录里面加测试例子

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个其实是因为不能在 _testdata 里面加所以才写成这样的,clearminmax 都需要 go.mod 里的 go version 至少是 1.21

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

另外加的话是不是也应该是加在 cl/_testrt/builtin 里才对?看目录结构感觉是的

prog := NewProgram(nil)
pkg := prog.NewPackage("bar", "foo/bar")

params := types.NewTuple(
types.NewVar(0, nil, "a", types.Typ[types.Int]),
types.NewVar(0, nil, "b", types.Typ[types.Int]),
types.NewVar(0, nil, "c", types.Typ[types.Int]),
)
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
fn := pkg.NewFunc("fn", sig, InGo)

b := fn.MakeBody(1)
result := b.compareSelect(token.GTR, fn.Param(0), fn.Param(1), fn.Param(2))
b.Return(result)

assertPkg(t, pkg, `; ModuleID = 'foo/bar'
source_filename = "foo/bar"

define i64 @fn(i64 %0, i64 %1, i64 %2) {
_llgo_0:
%3 = icmp sgt i64 %0, %1
%4 = select i1 %3, i64 %0, i64 %1
%5 = icmp sgt i64 %4, %2
%6 = select i1 %5, i64 %4, i64 %2
ret i64 %6
}
`)
}
Loading