Files
llgo/ssa/expr.go

1370 lines
38 KiB
Go
Raw Normal View History

2024-04-18 01:18:41 +08:00
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ssa
2024-04-18 15:03:10 +08:00
import (
2024-04-21 15:12:57 +08:00
"bytes"
"fmt"
2024-04-20 22:05:45 +08:00
"go/constant"
2024-04-18 22:18:43 +08:00
"go/token"
2024-04-19 00:05:57 +08:00
"go/types"
2024-04-21 15:12:57 +08:00
"log"
2024-04-18 22:18:43 +08:00
2024-05-10 13:25:36 +08:00
"github.com/goplus/llgo/internal/abi"
2024-04-18 15:03:10 +08:00
"github.com/goplus/llvm"
)
2024-04-18 22:18:43 +08:00
// -----------------------------------------------------------------------------
type Expr struct {
impl llvm.Value
Type
}
2024-05-01 07:26:51 +08:00
var Nil Expr // Zero value is a nil Expr
2024-04-30 08:23:55 +08:00
// IsNil checks if the expression is nil or not.
func (v Expr) IsNil() bool {
return v.Type == nil
}
2024-04-27 20:45:55 +08:00
// Do evaluates the delay expression and returns the result.
2024-05-02 07:56:52 +08:00
func (v Expr) Do(b Builder) Expr {
2024-05-02 07:37:31 +08:00
switch vt := v.Type; vt.kind {
case vkPhisExpr:
2024-05-05 12:11:51 +08:00
e := vt.raw.Type.(*phisExprTy)
return b.aggregateValue(e.Type, e.phis...)
2024-04-27 20:45:55 +08:00
}
return v
}
2024-05-02 07:37:31 +08:00
// -----------------------------------------------------------------------------
type phisExprTy struct {
phis []llvm.Value
2024-05-02 07:56:52 +08:00
Type
2024-05-02 07:37:31 +08:00
}
func (p phisExprTy) Underlying() types.Type {
panic("don't call")
}
func (p phisExprTy) String() string {
return "phisExpr"
}
2024-05-02 07:56:52 +08:00
func phisExpr(t Type, phis []llvm.Value) Expr {
2024-05-05 12:11:51 +08:00
return Expr{Type: &aType{raw: rawType{&phisExprTy{phis, t}}, kind: vkPhisExpr}}
2024-05-02 07:37:31 +08:00
}
// -----------------------------------------------------------------------------
2024-04-28 07:08:01 +08:00
// Null returns a null constant expression.
2024-04-21 00:22:39 +08:00
func (p Program) Null(t Type) Expr {
return Expr{llvm.ConstNull(t.ll), t}
}
2024-05-15 08:44:00 +08:00
// PyNull returns a null *PyObject constant expression.
func (p Program) PyNull() Expr {
return p.Null(p.PyObjectPtr())
}
2024-04-28 07:08:01 +08:00
// BoolVal returns a boolean constant expression.
2024-04-20 22:05:45 +08:00
func (p Program) BoolVal(v bool) Expr {
t := p.Bool()
var bv uint64
if v {
bv = 1
}
ret := llvm.ConstInt(t.ll, bv, v)
return Expr{ret, t}
}
2024-04-28 07:08:01 +08:00
// IntVal returns an integer constant expression.
2024-04-21 15:12:57 +08:00
func (p Program) IntVal(v uint64, t Type) Expr {
ret := llvm.ConstInt(t.ll, v, false)
2024-04-20 22:30:38 +08:00
return Expr{ret, t}
}
2024-04-29 22:19:08 +08:00
func (p Program) FloatVal(v float64, t Type) Expr {
ret := llvm.ConstFloat(t.ll, v)
return Expr{ret, t}
}
2024-04-28 07:08:01 +08:00
// Val returns a constant expression.
2024-04-19 00:05:57 +08:00
func (p Program) Val(v interface{}) Expr {
switch v := v.(type) {
case int:
2024-04-21 15:12:57 +08:00
return p.IntVal(uint64(v), p.Int())
2024-04-28 07:08:01 +08:00
case uintptr:
return p.IntVal(uint64(v), p.Uintptr())
2024-04-20 22:05:45 +08:00
case bool:
return p.BoolVal(v)
2024-04-19 00:05:57 +08:00
case float64:
t := p.Float64()
ret := llvm.ConstFloat(t.ll, v)
return Expr{ret, t}
}
panic("todo")
}
2024-04-28 07:08:01 +08:00
// Const returns a constant expression.
2024-04-21 15:12:57 +08:00
func (b Builder) Const(v constant.Value, typ Type) Expr {
2024-04-29 13:59:06 +08:00
prog := b.Prog
2024-04-28 06:23:21 +08:00
if v == nil {
return prog.Null(typ)
2024-04-28 06:23:21 +08:00
}
2024-05-05 12:11:51 +08:00
raw := typ.raw.Type
switch t := raw.(type) {
2024-04-20 22:05:45 +08:00
case *types.Basic:
2024-04-21 15:12:57 +08:00
kind := t.Kind()
switch {
case kind == types.Bool:
return prog.BoolVal(constant.BoolVal(v))
2024-04-30 13:53:04 +08:00
case kind >= types.Int && kind <= types.Int64:
2024-04-30 11:19:42 +08:00
if v, exact := constant.Int64Val(v); exact {
return prog.IntVal(uint64(v), typ)
2024-04-20 22:30:38 +08:00
}
2024-04-30 13:53:04 +08:00
case kind >= types.Uint && kind <= types.Uintptr:
if v, exact := constant.Uint64Val(v); exact {
return prog.IntVal(v, typ)
}
2024-04-29 22:19:08 +08:00
case kind == types.Float32 || kind == types.Float64:
if v, exact := constant.Float64Val(v); exact {
return prog.FloatVal(v, typ)
}
case kind == types.String:
2024-04-30 18:37:31 +08:00
return b.Str(constant.StringVal(v))
2024-04-20 22:05:45 +08:00
}
}
2024-05-05 12:11:51 +08:00
panic(fmt.Sprintf("unsupported Const: %v, %v", v, raw))
2024-04-20 22:05:45 +08:00
}
2024-05-03 23:10:02 +08:00
// SizeOf returns the size of a type.
func (b Builder) SizeOf(t Type, n ...int64) Expr {
prog := b.Prog
size := prog.SizeOf(t, n...)
return prog.IntVal(size, prog.Uintptr())
}
2024-04-30 08:23:55 +08:00
// CStr returns a c-style string constant expression.
func (b Builder) CStr(v string) Expr {
return Expr{llvm.CreateGlobalStringPtr(b.impl, v), b.Prog.CStr()}
2024-04-29 14:34:26 +08:00
}
2024-04-30 18:37:31 +08:00
// Str returns a Go string constant expression.
func (b Builder) Str(v string) (ret Expr) {
prog := b.Prog
2024-04-30 18:37:31 +08:00
cstr := b.CStr(v)
ret = b.InlineCall(b.Func.Pkg.rtFunc("NewString"), cstr, prog.Val(len(v)))
ret.Type = prog.String()
return
2024-04-30 18:37:31 +08:00
}
2024-04-19 00:05:57 +08:00
// -----------------------------------------------------------------------------
2024-04-18 22:18:43 +08:00
const (
mathOpBase = token.ADD
mathOpLast = token.REM
)
var mathOpToLLVM = []llvm.Opcode{
int(token.ADD-mathOpBase)<<2 | vkSigned: llvm.Add,
int(token.ADD-mathOpBase)<<2 | vkUnsigned: llvm.Add,
int(token.ADD-mathOpBase)<<2 | vkFloat: llvm.FAdd,
int(token.SUB-mathOpBase)<<2 | vkSigned: llvm.Sub,
int(token.SUB-mathOpBase)<<2 | vkUnsigned: llvm.Sub,
int(token.SUB-mathOpBase)<<2 | vkFloat: llvm.FSub,
int(token.MUL-mathOpBase)<<2 | vkSigned: llvm.Mul,
int(token.MUL-mathOpBase)<<2 | vkUnsigned: llvm.Mul,
int(token.MUL-mathOpBase)<<2 | vkFloat: llvm.FMul,
int(token.QUO-mathOpBase)<<2 | vkSigned: llvm.SDiv,
int(token.QUO-mathOpBase)<<2 | vkUnsigned: llvm.UDiv,
int(token.QUO-mathOpBase)<<2 | vkFloat: llvm.FDiv,
int(token.REM-mathOpBase)<<2 | vkSigned: llvm.SRem,
int(token.REM-mathOpBase)<<2 | vkUnsigned: llvm.URem,
int(token.REM-mathOpBase)<<2 | vkFloat: llvm.FRem,
}
func mathOpIdx(op token.Token, x valueKind) int {
return int(op-mathOpBase)<<2 | x
}
// ADD SUB MUL QUO REM + - * / %
func isMathOp(op token.Token) bool {
return op >= mathOpBase && op <= mathOpLast
}
const (
logicOpBase = token.AND
logicOpLast = token.AND_NOT
)
var logicOpToLLVM = []llvm.Opcode{
token.AND - logicOpBase: llvm.And,
token.OR - logicOpBase: llvm.Or,
token.XOR - logicOpBase: llvm.Xor,
token.SHL - logicOpBase: llvm.Shl,
2024-04-20 22:05:45 +08:00
token.SHR - logicOpBase: llvm.AShr, // Arithmetic Shift Right
2024-04-18 22:18:43 +08:00
}
// AND OR XOR SHL SHR AND_NOT & | ^ << >> &^
func isLogicOp(op token.Token) bool {
return op >= logicOpBase && op <= logicOpLast
}
const (
predOpBase = token.EQL
predOpLast = token.GEQ
)
var intPredOpToLLVM = []llvm.IntPredicate{
token.EQL - predOpBase: llvm.IntEQ,
token.NEQ - predOpBase: llvm.IntNE,
token.LSS - predOpBase: llvm.IntSLT,
token.LEQ - predOpBase: llvm.IntSLE,
token.GTR - predOpBase: llvm.IntSGT,
token.GEQ - predOpBase: llvm.IntSGE,
}
var uintPredOpToLLVM = []llvm.IntPredicate{
token.EQL - predOpBase: llvm.IntEQ,
token.NEQ - predOpBase: llvm.IntNE,
token.LSS - predOpBase: llvm.IntULT,
token.LEQ - predOpBase: llvm.IntULE,
token.GTR - predOpBase: llvm.IntUGT,
token.GEQ - predOpBase: llvm.IntUGE,
}
var floatPredOpToLLVM = []llvm.FloatPredicate{
token.EQL - predOpBase: llvm.FloatOEQ,
token.NEQ - predOpBase: llvm.FloatONE,
token.LSS - predOpBase: llvm.FloatOLT,
token.LEQ - predOpBase: llvm.FloatOLE,
token.GTR - predOpBase: llvm.FloatOGT,
token.GEQ - predOpBase: llvm.FloatOGE,
}
2024-05-10 20:58:35 +08:00
var boolPredOpToLLVM = []llvm.IntPredicate{
token.EQL - predOpBase: llvm.IntEQ,
token.NEQ - predOpBase: llvm.IntNE,
}
2024-04-18 22:18:43 +08:00
// EQL NEQ LSS LEQ GTR GEQ == != < <= < >=
func isPredOp(op token.Token) bool {
return op >= predOpBase && op <= predOpLast
}
2024-04-20 17:31:49 +08:00
// The BinOp instruction yields the result of binary operation (x op y).
// op can be:
2024-04-18 22:18:43 +08:00
// ADD SUB MUL QUO REM + - * / %
// AND OR XOR SHL SHR AND_NOT & | ^ << >> &^
// EQL NEQ LSS LEQ GTR GEQ == != < <= < >=
func (b Builder) BinOp(op token.Token, x, y Expr) Expr {
2024-04-21 15:12:57 +08:00
if debugInstr {
log.Printf("BinOp %d, %v, %v\n", op, x.impl, y.impl)
}
2024-04-18 22:18:43 +08:00
switch {
case isMathOp(op): // op: + - * / %
kind := x.kind
switch kind {
2024-05-01 22:00:34 +08:00
case vkString:
if op == token.ADD {
pkg := b.Func.Pkg
2024-05-14 15:58:33 +08:00
return Expr{b.InlineCall(pkg.rtFunc("StringCat"), x, y).impl, x.Type}
2024-05-01 22:00:34 +08:00
}
case vkComplex:
default:
idx := mathOpIdx(op, kind)
if llop := mathOpToLLVM[idx]; llop != 0 {
return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type}
}
2024-04-18 22:18:43 +08:00
}
case isLogicOp(op): // op: & | ^ << >> &^
if op == token.AND_NOT {
2024-05-10 21:30:17 +08:00
return Expr{b.impl.CreateAnd(x.impl, b.impl.CreateNot(y.impl, ""), ""), x.Type}
2024-04-18 22:18:43 +08:00
}
kind := x.kind
llop := logicOpToLLVM[op-logicOpBase]
if op == token.SHR && kind == vkUnsigned {
2024-04-20 22:05:45 +08:00
llop = llvm.LShr // Logical Shift Right
2024-04-18 22:18:43 +08:00
}
return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type}
case isPredOp(op): // op: == != < <= < >=
2024-04-29 13:59:06 +08:00
tret := b.Prog.Bool()
2024-04-18 22:18:43 +08:00
kind := x.kind
switch kind {
case vkSigned:
pred := intPredOpToLLVM[op-predOpBase]
return Expr{llvm.CreateICmp(b.impl, pred, x.impl, y.impl), tret}
2024-04-28 07:08:01 +08:00
case vkUnsigned, vkPtr:
2024-04-18 22:18:43 +08:00
pred := uintPredOpToLLVM[op-predOpBase]
return Expr{llvm.CreateICmp(b.impl, pred, x.impl, y.impl), tret}
case vkFloat:
pred := floatPredOpToLLVM[op-predOpBase]
2024-04-22 15:09:08 +08:00
return Expr{llvm.CreateFCmp(b.impl, pred, x.impl, y.impl), tret}
2024-05-10 20:58:35 +08:00
case vkBool:
pred := boolPredOpToLLVM[op-predOpBase]
return Expr{llvm.CreateICmp(b.impl, pred, x.impl, y.impl), tret}
case vkString, vkComplex:
2024-04-18 22:18:43 +08:00
panic("todo")
}
}
panic("todo")
}
2024-04-20 17:31:49 +08:00
// The UnOp instruction yields the result of (op x).
// ARROW is channel receive.
// MUL is pointer indirection (load).
// XOR is bitwise complement.
// SUB is negation.
// NOT is logical negation.
2024-05-07 09:55:51 +08:00
func (b Builder) UnOp(op token.Token, x Expr) (ret Expr) {
if debugInstr {
log.Printf("UnOp %v, %v\n", op, x.impl)
}
2024-04-20 17:31:49 +08:00
switch op {
case token.MUL:
return b.Load(x)
2024-05-07 09:55:51 +08:00
case token.SUB:
switch t := x.Type.raw.Underlying().(type) {
case *types.Basic:
ret.Type = x.Type
if t.Info()&types.IsInteger != 0 {
ret.impl = b.impl.CreateNeg(x.impl, "")
} else if t.Info()&types.IsFloat != 0 {
ret.impl = b.impl.CreateFNeg(x.impl, "")
} else {
panic("todo")
}
default:
panic("unreachable")
}
case token.NOT:
ret.Type = x.Type
ret.impl = b.impl.CreateNot(x.impl, "")
case token.XOR:
ret.Type = x.Type
ret.impl = b.impl.CreateXor(x.impl, llvm.ConstInt(x.Type.ll, ^uint64(0), false), "")
case token.ARROW:
panic("todo")
2024-04-20 17:31:49 +08:00
}
2024-05-07 09:55:51 +08:00
return
2024-04-20 17:31:49 +08:00
}
2024-04-30 10:34:10 +08:00
// -----------------------------------------------------------------------------
func checkExpr(v Expr, t types.Type, b Builder) Expr {
2024-05-05 18:20:51 +08:00
if t, ok := t.(*types.Struct); ok && isClosure(t) {
if v.kind != vkClosure {
2024-05-05 18:48:09 +08:00
return b.Func.Pkg.closureStub(b, t, v)
}
}
return v
}
2024-05-05 18:20:51 +08:00
func llvmParamsEx(data Expr, vals []Expr, params *types.Tuple, b Builder) (ret []llvm.Value) {
if data.IsNil() {
return llvmParams(0, vals, params, b)
}
ret = llvmParams(1, vals, params, b)
ret[0] = data.impl
return
}
func llvmParams(base int, vals []Expr, params *types.Tuple, b Builder) (ret []llvm.Value) {
n := params.Len()
2024-05-05 15:07:10 +08:00
if n > 0 {
2024-05-05 18:20:51 +08:00
ret = make([]llvm.Value, len(vals)+base)
for idx, v := range vals {
i := base + idx
2024-05-05 15:07:10 +08:00
if i < n {
v = checkExpr(v, params.At(i).Type(), b)
}
ret[i] = v.impl
}
2024-04-30 10:34:10 +08:00
}
2024-05-05 15:07:10 +08:00
return
2024-04-30 10:34:10 +08:00
}
func llvmFields(vals []Expr, t *types.Struct, b Builder) (ret []llvm.Value) {
n := t.NumFields()
if n > 0 {
ret = make([]llvm.Value, len(vals))
for i, v := range vals {
if i < n {
v = checkExpr(v, t.Field(i).Type(), b)
}
ret[i] = v.impl
}
}
return
}
func llvmDelayValues(f func(i int) Expr, n int) []llvm.Value {
ret := make([]llvm.Value, n)
for i := 0; i < n; i++ {
ret[i] = f(i).impl
2024-05-02 07:37:31 +08:00
}
return ret
}
2024-04-30 10:34:10 +08:00
func llvmBlocks(bblks []BasicBlock) []llvm.BasicBlock {
ret := make([]llvm.BasicBlock, len(bblks))
for i, v := range bblks {
ret[i] = v.impl
}
return ret
}
// Phi represents a phi node.
type Phi struct {
Expr
}
// AddIncoming adds incoming values to a phi node.
func (p Phi) AddIncoming(b Builder, bblks []BasicBlock, f func(i int) Expr) {
2024-05-02 07:37:31 +08:00
bs := llvmBlocks(bblks)
if p.kind != vkPhisExpr { // normal phi node
vs := llvmDelayValues(f, len(bblks))
2024-05-02 07:37:31 +08:00
p.impl.AddIncoming(vs, bs)
return
}
2024-05-05 12:11:51 +08:00
e := p.raw.Type.(*phisExprTy)
phis := e.phis
vals := make([][]llvm.Value, len(phis))
for iblk, blk := range bblks {
last := blk.impl.LastInstruction()
b.impl.SetInsertPointBefore(last)
impl := b.impl
val := f(iblk).impl
for i := range phis {
if iblk == 0 {
vals[i] = make([]llvm.Value, len(bblks))
}
vals[i][iblk] = llvm.CreateExtractValue(impl, val, i)
}
}
for i, phi := range phis {
phi.AddIncoming(vals[i], bs)
2024-05-02 07:37:31 +08:00
}
2024-04-30 10:34:10 +08:00
}
// Phi returns a phi node.
func (b Builder) Phi(t Type) Phi {
2024-05-02 07:37:31 +08:00
impl := b.impl
2024-05-05 12:11:51 +08:00
switch tund := t.raw.Type.Underlying().(type) {
2024-05-02 07:37:31 +08:00
case *types.Basic:
kind := tund.Kind()
switch kind {
case types.String:
prog := b.Prog
phis := make([]llvm.Value, 2)
phis[0] = llvm.CreatePHI(impl, prog.tyVoidPtr())
phis[1] = llvm.CreatePHI(impl, prog.tyInt())
2024-05-02 07:56:52 +08:00
return Phi{phisExpr(t, phis)}
2024-05-02 07:37:31 +08:00
}
2024-05-05 12:11:51 +08:00
case *types.Struct:
panic("todo")
2024-05-02 07:37:31 +08:00
}
phi := llvm.CreatePHI(impl, t.ll)
return Phi{Expr{phi, t}}
2024-04-30 10:34:10 +08:00
}
// -----------------------------------------------------------------------------
2024-05-07 16:05:18 +08:00
// Advance returns the pointer ptr advanced by offset.
2024-05-01 23:57:19 +08:00
func (b Builder) Advance(ptr Expr, offset Expr) Expr {
if debugInstr {
log.Printf("Advance %v, %v\n", ptr.impl, offset.impl)
}
2024-05-07 16:05:18 +08:00
var elem llvm.Type
var prog = b.Prog
2024-05-07 16:12:27 +08:00
switch t := ptr.raw.Type.(type) {
case *types.Basic: // void
2024-05-07 16:05:18 +08:00
elem = prog.tyInt8()
2024-05-07 16:12:27 +08:00
default:
elem = prog.rawType(t.(*types.Pointer).Elem()).ll
2024-05-07 16:05:18 +08:00
}
ret := llvm.CreateGEP(b.impl, elem, ptr.impl, []llvm.Value{offset.impl})
2024-05-01 23:57:19 +08:00
return Expr{ret, ptr.Type}
}
2024-04-20 17:31:49 +08:00
// Load returns the value at the pointer ptr.
func (b Builder) Load(ptr Expr) Expr {
2024-04-21 15:12:57 +08:00
if debugInstr {
2024-04-25 21:44:23 +08:00
log.Printf("Load %v\n", ptr.impl)
2024-04-21 15:12:57 +08:00
}
2024-04-29 13:59:06 +08:00
telem := b.Prog.Elem(ptr.Type)
2024-04-20 17:31:49 +08:00
return Expr{llvm.CreateLoad(b.impl, telem.ll, ptr.impl), telem}
}
2024-04-20 22:05:45 +08:00
// Store stores val at the pointer ptr.
func (b Builder) Store(ptr, val Expr) Builder {
2024-05-06 16:10:26 +08:00
raw := ptr.raw.Type
2024-04-21 15:12:57 +08:00
if debugInstr {
2024-05-06 16:10:26 +08:00
log.Printf("Store %v, %v, %v\n", raw, ptr.impl, val.impl)
2024-04-21 15:12:57 +08:00
}
2024-05-06 16:10:26 +08:00
val = checkExpr(val, raw.(*types.Pointer).Elem(), b)
2024-04-20 22:05:45 +08:00
b.impl.CreateStore(val.impl, ptr.impl)
return b
}
func (b Builder) aggregateAlloc(t Type, flds ...llvm.Value) llvm.Value {
prog := b.Prog
pkg := b.Func.Pkg
size := prog.SizeOf(t)
ptr := b.InlineCall(pkg.rtFunc("AllocU"), prog.IntVal(size, prog.Uintptr())).impl
tll := t.ll
impl := b.impl
for i, fld := range flds {
impl.CreateStore(fld, llvm.CreateStructGEP(impl, tll, ptr, i))
}
return ptr
}
// aggregateValue yields the value of the aggregate X with the fields
func (b Builder) aggregateValue(t Type, flds ...llvm.Value) Expr {
tll := t.ll
impl := b.impl
ptr := llvm.CreateAlloca(impl, tll)
for i, fld := range flds {
impl.CreateStore(fld, llvm.CreateStructGEP(impl, tll, ptr, i))
}
return Expr{llvm.CreateLoad(b.impl, tll, ptr), t}
}
2024-05-04 19:44:52 +08:00
// The MakeClosure instruction yields a closure value whose code is
// Fn and whose free variables' values are supplied by Bindings.
//
// Type() returns a (possibly named) *types.Signature.
//
// Example printed form:
//
// t0 = make closure anon@1.2 [x y z]
// t1 = make closure bound$(main.I).add [i]
func (b Builder) MakeClosure(fn Expr, bindings []Expr) Expr {
if debugInstr {
log.Printf("MakeClosure %v, %v\n", fn, bindings)
}
prog := b.Prog
tfn := fn.Type
sig := tfn.raw.Type.(*types.Signature)
tctx := sig.Params().At(0).Type().Underlying().(*types.Pointer).Elem().(*types.Struct)
flds := llvmFields(bindings, tctx, b)
data := b.aggregateAlloc(prog.rawType(tctx), flds...)
return b.aggregateValue(prog.Closure(tfn), fn.impl, data)
2024-05-04 19:44:52 +08:00
}
2024-04-27 07:47:10 +08:00
// The FieldAddr instruction yields the address of Field of *struct X.
//
// The field is identified by its index within the field list of the
// struct type of X.
//
// Dynamically, this instruction panics if X evaluates to a nil
// pointer.
//
// Type() returns a (possibly named) *types.Pointer.
//
// Example printed form:
//
// t1 = &t0.name [#1]
func (b Builder) FieldAddr(x Expr, idx int) Expr {
if debugInstr {
log.Printf("FieldAddr %v, %d\n", x.impl, idx)
}
2024-04-29 13:59:06 +08:00
prog := b.Prog
2024-04-27 07:47:10 +08:00
tstruc := prog.Elem(x.Type)
telem := prog.Field(tstruc, idx)
pt := prog.Pointer(telem)
2024-04-27 08:17:46 +08:00
return Expr{llvm.CreateStructGEP(b.impl, tstruc.ll, x.impl, idx), pt}
2024-04-27 07:47:10 +08:00
}
2024-05-04 07:21:07 +08:00
// The Field instruction yields the value of Field of struct X.
func (b Builder) Field(x Expr, idx int) Expr {
if debugInstr {
log.Printf("Field %v, %d\n", x.impl, idx)
}
return b.getField(x, idx)
}
func (b Builder) getField(x Expr, idx int) Expr {
2024-05-05 12:11:51 +08:00
tfld := b.Prog.Field(x.Type, idx)
fld := llvm.CreateExtractValue(b.impl, x.impl, idx)
return Expr{fld, tfld}
2024-05-04 07:21:07 +08:00
}
2024-05-12 15:42:50 +08:00
// StringData returns the data pointer of a string.
func (b Builder) StringData(x Expr) Expr {
if debugInstr {
log.Printf("StringData %v\n", x.impl)
}
prog := b.Prog
ptr := llvm.CreateExtractValue(b.impl, x.impl, 0)
return Expr{ptr, prog.CStr()}
}
// StringLen returns the length of a string.
func (b Builder) StringLen(x Expr) Expr {
if debugInstr {
log.Printf("StringLen %v\n", x.impl)
}
prog := b.Prog
ptr := llvm.CreateExtractValue(b.impl, x.impl, 1)
return Expr{ptr, prog.Int()}
}
2024-05-15 10:29:06 +08:00
// SliceData returns the data pointer of a slice.
func (b Builder) SliceData(x Expr) Expr {
if debugInstr {
log.Printf("SliceData %v\n", x.impl)
}
prog := b.Prog
ptr := llvm.CreateExtractValue(b.impl, x.impl, 0)
return Expr{ptr, prog.CStr()}
}
// SliceLen returns the length of a slice.
func (b Builder) SliceLen(x Expr) Expr {
if debugInstr {
log.Printf("SliceLen %v\n", x.impl)
}
prog := b.Prog
ptr := llvm.CreateExtractValue(b.impl, x.impl, 1)
return Expr{ptr, prog.Int()}
}
// SliceCap returns the length of a slice cap.
func (b Builder) SliceCap(x Expr) Expr {
if debugInstr {
log.Printf("SliceCap %v\n", x.impl)
}
prog := b.Prog
ptr := llvm.CreateExtractValue(b.impl, x.impl, 2)
return Expr{ptr, prog.Int()}
}
2024-04-21 15:12:57 +08:00
// The IndexAddr instruction yields the address of the element at
// index `idx` of collection `x`. `idx` is an integer expression.
//
// The elements of maps and strings are not addressable; use Lookup (map),
// Index (string), or MapUpdate instead.
//
// Dynamically, this instruction panics if `x` evaluates to a nil *array
// pointer.
//
// Example printed form:
//
// t2 = &t0[t1]
func (b Builder) IndexAddr(x, idx Expr) Expr {
if debugInstr {
log.Printf("IndexAddr %v, %v\n", x.impl, idx.impl)
}
2024-04-29 13:59:06 +08:00
prog := b.Prog
2024-04-21 15:12:57 +08:00
telem := prog.Index(x.Type)
pt := prog.Pointer(telem)
2024-05-05 12:11:51 +08:00
switch x.raw.Type.Underlying().(type) {
2024-04-30 16:15:36 +08:00
case *types.Slice:
2024-05-15 10:29:06 +08:00
ptr := b.SliceData(x)
2024-04-30 16:15:36 +08:00
indices := []llvm.Value{idx.impl}
return Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, ptr.impl, indices), pt}
}
2024-04-30 16:19:25 +08:00
// case *types.Pointer:
indices := []llvm.Value{idx.impl}
return Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, x.impl, indices), pt}
2024-04-21 15:12:57 +08:00
}
2024-05-01 21:56:11 +08:00
// The Index instruction yields element Index of collection X, an array,
// string or type parameter containing an array, a string, a pointer to an,
// array or a slice.
//
// Example printed form:
//
// t2 = t0[t1]
func (b Builder) Index(x, idx Expr, addr func(Expr) Expr) Expr {
if debugInstr {
log.Printf("Index %v, %v\n", x.impl, idx.impl)
}
prog := b.Prog
var telem Type
var ptr Expr
2024-05-05 12:11:51 +08:00
switch t := x.raw.Type.Underlying().(type) {
2024-05-01 21:56:11 +08:00
case *types.Basic:
2024-05-05 15:07:10 +08:00
if t.Kind() != types.String {
2024-05-01 21:56:11 +08:00
panic(fmt.Errorf("invalid operation: cannot index %v", t))
}
2024-05-05 12:11:51 +08:00
telem = prog.rawType(types.Typ[types.Byte])
2024-05-12 15:42:50 +08:00
ptr = b.StringData(x)
2024-05-01 21:56:11 +08:00
case *types.Array:
telem = prog.Index(x.Type)
if addr != nil {
ptr = addr(x)
} else {
2024-05-03 23:10:02 +08:00
size := b.SizeOf(telem, t.Len())
ptr = b.Alloca(size)
2024-05-01 21:56:11 +08:00
b.Store(ptr, x)
}
}
pt := prog.Pointer(telem)
indices := []llvm.Value{idx.impl}
buf := Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, ptr.impl, indices), pt}
return b.Load(buf)
}
2024-05-06 01:17:37 +08:00
// The Lookup instruction yields element Index of collection map X.
// Index is the appropriate key type.
//
// If CommaOk, the result is a 2-tuple of the value above and a
// boolean indicating the result of a map membership test for the key.
// The components of the tuple are accessed using Extract.
//
// Example printed form:
//
// t2 = t0[t1]
// t5 = t3[t4],ok
func (b Builder) Lookup(x, key Expr, commaOk bool) (ret Expr) {
if debugInstr {
log.Printf("Lookup %v, %v, %v\n", x.impl, key.impl, commaOk)
}
// TODO(xsw)
// panic("todo")
return
}
2024-04-30 08:23:55 +08:00
// The Slice instruction yields a slice of an existing string, slice
// or *array X between optional integer bounds Low and High.
//
// Dynamically, this instruction panics if X evaluates to a nil *array
// pointer.
//
// Type() returns string if the type of X was string, otherwise a
// *types.Slice with the same element type as X.
//
// Example printed form:
//
// t1 = slice t0[1:]
func (b Builder) Slice(x, low, high, max Expr) (ret Expr) {
if debugInstr {
log.Printf("Slice %v, %v, %v\n", x.impl, low.impl, high.impl)
}
prog := b.Prog
pkg := b.Func.Pkg
2024-05-03 10:30:07 +08:00
var nCap Expr
var nEltSize Expr
var base Expr
if low.IsNil() {
low = prog.IntVal(0, prog.Int())
}
2024-05-05 12:11:51 +08:00
switch t := x.raw.Type.Underlying().(type) {
2024-05-03 10:30:07 +08:00
case *types.Basic:
2024-05-03 23:10:02 +08:00
if t.Kind() != types.String {
2024-05-03 10:30:07 +08:00
panic(fmt.Errorf("invalid operation: cannot slice %v", t))
}
if high.IsNil() {
2024-05-12 15:42:50 +08:00
high = b.StringLen(x)
2024-05-03 10:30:07 +08:00
}
ret.Type = x.Type
ret.impl = b.InlineCall(pkg.rtFunc("NewStringSlice"), x, low, high).impl
return
case *types.Slice:
2024-05-03 23:10:02 +08:00
nEltSize = b.SizeOf(prog.Index(x.Type))
2024-05-15 10:29:06 +08:00
nCap = b.SliceCap(x)
2024-05-03 10:30:07 +08:00
if high.IsNil() {
2024-05-15 10:29:06 +08:00
high = b.SliceCap(x)
2024-05-03 10:30:07 +08:00
}
ret.Type = x.Type
2024-05-15 10:29:06 +08:00
base = b.SliceData(x)
2024-04-30 08:23:55 +08:00
case *types.Pointer:
telem := t.Elem()
switch te := telem.Underlying().(type) {
case *types.Array:
2024-05-05 12:11:51 +08:00
elem := prog.rawType(te.Elem())
2024-05-03 23:10:02 +08:00
ret.Type = prog.Slice(elem)
nEltSize = b.SizeOf(elem)
2024-05-03 10:30:07 +08:00
nCap = prog.IntVal(uint64(te.Len()), prog.Int())
if high.IsNil() {
high = nCap
2024-04-30 08:23:55 +08:00
}
2024-05-03 10:30:07 +08:00
base = x
2024-04-30 08:23:55 +08:00
}
}
2024-05-03 10:30:07 +08:00
if max.IsNil() {
max = nCap
}
ret.impl = b.InlineCall(pkg.rtFunc("NewSlice3"), base, nEltSize, nCap, low, high, max).impl
return
2024-04-30 08:23:55 +08:00
}
2024-04-30 10:34:10 +08:00
// -----------------------------------------------------------------------------
2024-05-01 07:26:51 +08:00
// The MakeMap instruction creates a new hash-table-based map object
// and yields a value of kind map.
//
// t is a (possibly named) *types.Map.
//
// Example printed form:
//
// t1 = make map[string]int t0
// t1 = make StringIntMap t0
func (b Builder) MakeMap(t Type, nReserve Expr) (ret Expr) {
if debugInstr {
2024-05-05 12:11:51 +08:00
log.Printf("MakeMap %v, %v\n", t.RawType(), nReserve.impl)
2024-05-01 07:26:51 +08:00
}
pkg := b.Func.Pkg
2024-05-01 07:26:51 +08:00
ret.Type = t
ret.impl = b.InlineCall(pkg.rtFunc("MakeSmallMap")).impl
// TODO(xsw): nReserve
return
}
2024-05-03 23:10:02 +08:00
// The MakeSlice instruction yields a slice of length Len backed by a
// newly allocated array of length Cap.
//
// Both Len and Cap must be non-nil Values of integer type.
//
// (Alloc(types.Array) followed by Slice will not suffice because
// Alloc can only create arrays of constant length.)
//
// Type() returns a (possibly named) *types.Slice.
//
// Example printed form:
//
// t1 = make []string 1:int t0
// t1 = make StringSlice 1:int t0
func (b Builder) MakeSlice(t Type, len, cap Expr) (ret Expr) {
if debugInstr {
2024-05-05 12:11:51 +08:00
log.Printf("MakeSlice %v, %v, %v\n", t.RawType(), len.impl, cap.impl)
2024-05-03 23:10:02 +08:00
}
pkg := b.Func.Pkg
2024-05-03 23:10:02 +08:00
if cap.IsNil() {
cap = len
}
2024-05-03 23:49:52 +08:00
elemSize := b.SizeOf(b.Prog.Index(t))
2024-05-03 23:10:02 +08:00
size := b.BinOp(token.MUL, cap, elemSize)
ptr := b.InlineCall(pkg.rtFunc("AllocZ"), size)
ret.impl = b.InlineCall(pkg.rtFunc("NewSlice"), ptr, len, cap).impl
ret.Type = t
return
}
2024-05-01 07:26:51 +08:00
// -----------------------------------------------------------------------------
2024-04-21 15:12:57 +08:00
// The Alloc instruction reserves space for a variable of the given type,
// zero-initializes it, and yields its address.
//
// If heap is false, Alloc zero-initializes the same local variable in
// the call frame and returns its address; in this case the Alloc must
// be present in Function.Locals. We call this a "local" alloc.
//
// If heap is true, Alloc allocates a new zero-initialized variable
// each time the instruction is executed. We call this a "new" alloc.
//
// When Alloc is applied to a channel, map or slice type, it returns
// the address of an uninitialized (nil) reference of that kind; store
// the result of MakeSlice, MakeMap or MakeChan in that location to
// instantiate these types.
//
// Example printed form:
//
// t0 = local int
// t1 = new int
2024-05-05 12:11:51 +08:00
func (b Builder) Alloc(elem Type, heap bool) (ret Expr) {
2024-04-21 15:12:57 +08:00
if debugInstr {
2024-05-05 12:11:51 +08:00
log.Printf("Alloc %v, %v\n", elem.RawType(), heap)
2024-04-21 15:12:57 +08:00
}
2024-04-29 13:59:06 +08:00
prog := b.Prog
pkg := b.Func.Pkg
2024-05-03 23:10:02 +08:00
size := b.SizeOf(elem)
2024-04-21 15:12:57 +08:00
if heap {
2024-05-03 23:10:02 +08:00
ret = b.InlineCall(pkg.rtFunc("AllocZ"), size)
2024-04-28 10:39:11 +08:00
} else {
2024-05-03 23:10:02 +08:00
ret = Expr{llvm.CreateAlloca(b.impl, elem.ll), prog.VoidPtr()}
ret.impl = b.InlineCall(pkg.rtFunc("Zeroinit"), ret, size).impl
2024-04-21 15:12:57 +08:00
}
2024-05-05 12:11:51 +08:00
ret.Type = prog.Pointer(elem)
2024-04-21 15:12:57 +08:00
return
}
2024-04-29 17:58:10 +08:00
// Alloca allocates space for n bytes.
func (b Builder) Alloca(n Expr) (ret Expr) {
if debugInstr {
log.Printf("Alloca %v\n", n.impl)
}
prog := b.Prog
telem := prog.tyInt8()
ret.impl = llvm.CreateArrayAlloca(b.impl, telem, n.impl)
2024-05-05 12:11:51 +08:00
ret.Type = prog.VoidPtr()
2024-04-29 17:58:10 +08:00
return
}
2024-04-29 18:01:03 +08:00
/*
2024-04-29 17:58:10 +08:00
// ArrayAlloca reserves space for an array of n elements of type telem.
func (b Builder) ArrayAlloca(telem Type, n Expr) (ret Expr) {
if debugInstr {
log.Printf("ArrayAlloca %v, %v\n", telem.t, n.impl)
}
ret.impl = llvm.CreateArrayAlloca(b.impl, telem.ll, n.impl)
ret.Type = b.Prog.Pointer(telem)
return
}
2024-04-29 18:01:03 +08:00
*/
2024-04-29 17:58:10 +08:00
2024-04-30 18:22:56 +08:00
// AllocaCStr allocates space for copy it from a Go string.
func (b Builder) AllocaCStr(gostr Expr) (ret Expr) {
if debugInstr {
log.Printf("AllocaCStr %v\n", gostr.impl)
}
pkg := b.Func.Pkg
2024-05-12 15:42:50 +08:00
n := b.StringLen(gostr)
2024-04-30 18:22:56 +08:00
n1 := b.BinOp(token.ADD, n, b.Prog.Val(1))
cstr := b.Alloca(n1)
return b.InlineCall(pkg.rtFunc("CStrCopy"), cstr, gostr)
}
2024-04-30 10:34:10 +08:00
// -----------------------------------------------------------------------------
2024-04-26 00:31:02 +08:00
// The ChangeType instruction applies to X a value-preserving type
// change to Type().
//
// Type changes are permitted:
// - between a named type and its underlying type.
// - between two named types of the same underlying type.
// - between (possibly named) pointers to identical base types.
// - from a bidirectional channel to a read- or write-channel,
// optionally adding/removing a name.
// - between a type (t) and an instance of the type (tσ), i.e.
// Type() == σ(X.Type()) (or X.Type()== σ(Type())) where
// σ is the type substitution of Parent().TypeParams by
// Parent().TypeArgs.
//
// This operation cannot fail dynamically.
//
// Type changes may to be to or from a type parameter (or both). All
// types in the type set of X.Type() have a value-preserving type
// change to all types in the type set of Type().
//
// Example printed form:
//
// t1 = changetype *int <- IntPtr (t0)
func (b Builder) ChangeType(t Type, x Expr) (ret Expr) {
if debugInstr {
2024-05-05 12:11:51 +08:00
log.Printf("ChangeType %v, %v\n", t.RawType(), x.impl)
2024-04-26 00:31:02 +08:00
}
2024-05-05 12:11:51 +08:00
typ := t.raw.Type
2024-04-26 00:31:02 +08:00
switch typ.(type) {
2024-04-28 06:23:21 +08:00
default:
2024-05-05 12:11:51 +08:00
// TODO(xsw): remove instr name
2024-04-28 06:23:21 +08:00
ret.impl = b.impl.CreateBitCast(x.impl, t.ll, "bitCast")
2024-05-05 12:11:51 +08:00
ret.Type = b.Prog.rawType(typ)
2024-04-28 06:23:21 +08:00
return
}
}
// The Convert instruction yields the conversion of value X to type
// Type(). One or both of those types is basic (but possibly named).
//
// A conversion may change the value and representation of its operand.
// Conversions are permitted:
// - between real numeric types.
// - between complex numeric types.
// - between string and []byte or []rune.
// - between pointers and unsafe.Pointer.
// - between unsafe.Pointer and uintptr.
// - from (Unicode) integer to (UTF-8) string.
//
// A conversion may imply a type name change also.
//
// Conversions may to be to or from a type parameter. All types in
// the type set of X.Type() can be converted to all types in the type
// set of Type().
//
// This operation cannot fail dynamically.
//
// Conversions of untyped string/number/bool constants to a specific
// representation are eliminated during SSA construction.
//
// Example printed form:
//
// t1 = convert []byte <- string (t0)
func (b Builder) Convert(t Type, x Expr) (ret Expr) {
2024-05-08 12:25:56 +08:00
if debugInstr {
log.Printf("Convert %v <- %v\n", t.RawType(), x.RawType())
}
2024-05-05 12:11:51 +08:00
typ := t.raw.Type
ret.Type = b.Prog.rawType(typ)
2024-05-08 12:25:56 +08:00
switch typ := typ.Underlying().(type) {
2024-04-28 06:23:21 +08:00
case *types.Basic:
2024-05-08 12:25:56 +08:00
switch typ.Kind() {
case types.Uintptr:
ret.impl = castInt(b.impl, x.impl, t.ll)
2024-04-28 06:23:21 +08:00
return
2024-05-08 12:25:56 +08:00
case types.UnsafePointer:
ret.impl = castPtr(b.impl, x.impl, t.ll)
2024-04-28 06:23:21 +08:00
return
}
2024-05-08 12:25:56 +08:00
switch xtyp := x.RawType().Underlying().(type) {
case *types.Basic:
size := b.Prog.SizeOf(t)
xsize := b.Prog.SizeOf(x.Type)
if typ.Info()&types.IsInteger != 0 {
// int <- int/float
if xtyp.Info()&types.IsInteger != 0 {
// if xsize > size {
// ret.impl = b.impl.CreateTrunc(x.impl, t.ll, "")
// } else if typ.Info()&types.IsUnsigned != 0 {
// ret.impl = b.impl.CreateZExt(x.impl, t.ll, "")
// } else {
// ret.impl = b.impl.CreateSExt(x.impl, t.ll, "")
// }
ret.impl = castInt(b.impl, x.impl, t.ll)
return
} else if xtyp.Info()&types.IsFloat != 0 {
if typ.Info()&types.IsUnsigned != 0 {
ret.impl = b.impl.CreateFPToUI(x.impl, t.ll, "")
} else {
ret.impl = b.impl.CreateFPToSI(x.impl, t.ll, "")
}
return
}
} else if typ.Info()&types.IsFloat != 0 {
// float <- int/float
if xtyp.Info()&types.IsInteger != 0 {
if xtyp.Info()&types.IsUnsigned != 0 {
ret.impl = b.impl.CreateUIToFP(x.impl, t.ll, "")
} else {
ret.impl = b.impl.CreateSIToFP(x.impl, t.ll, "")
}
return
} else if xtyp.Info()&types.IsFloat != 0 {
if xsize > size {
ret.impl = b.impl.CreateFPTrunc(x.impl, t.ll, "")
} else {
ret.impl = b.impl.CreateFPExt(x.impl, t.ll, "")
}
return
}
}
}
2024-04-26 00:31:02 +08:00
case *types.Pointer:
ret.impl = castPtr(b.impl, x.impl, t.ll)
2024-04-26 00:31:02 +08:00
return
}
panic("todo")
}
func castInt(b llvm.Builder, x llvm.Value, t llvm.Type) llvm.Value {
xt := x.Type()
if xt.TypeKind() == llvm.PointerTypeKind {
2024-04-29 03:43:38 +08:00
return llvm.CreatePtrToInt(b, x, t)
}
if xt.IntTypeWidth() <= t.IntTypeWidth() {
2024-04-29 03:43:38 +08:00
return llvm.CreateIntCast(b, x, t)
}
2024-04-29 03:43:38 +08:00
return llvm.CreateTrunc(b, x, t)
}
func castPtr(b llvm.Builder, x llvm.Value, t llvm.Type) llvm.Value {
if x.Type().TypeKind() == llvm.PointerTypeKind {
2024-04-29 03:43:38 +08:00
return llvm.CreatePointerCast(b, x, t)
}
2024-04-29 03:43:38 +08:00
return llvm.CreateIntToPtr(b, x, t)
}
2024-04-27 20:45:55 +08:00
// MakeInterface constructs an instance of an interface type from a
// value of a concrete type.
//
// Use Program.MethodSets.MethodSet(X.Type()) to find the method-set
// of X, and Program.MethodValue(m) to find the implementation of a method.
//
// To construct the zero value of an interface type T, use:
//
// NewConst(constant.MakeNil(), T, pos)
//
// Example printed form:
//
// t1 = make interface{} <- int (42:int)
// t2 = make Stringer <- t0
2024-05-09 06:48:16 +08:00
func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) {
2024-05-05 12:11:51 +08:00
raw := tinter.raw.Type
2024-04-27 20:45:55 +08:00
if debugInstr {
2024-05-05 12:11:51 +08:00
log.Printf("MakeInterface %v, %v\n", raw, x.impl)
2024-04-27 20:45:55 +08:00
}
2024-05-09 06:48:16 +08:00
prog := b.Prog
pkg := b.Func.Pkg
switch tx := x.raw.Type.Underlying().(type) {
case *types.Basic:
kind := tx.Kind()
switch {
2024-05-10 13:25:36 +08:00
case kind >= types.Bool && kind <= types.Uintptr:
2024-05-09 06:48:16 +08:00
t := b.InlineCall(pkg.rtFunc("Basic"), prog.Val(int(kind)))
tptr := prog.Uintptr()
vptr := Expr{llvm.CreateIntCast(b.impl, x.impl, tptr.ll), tptr}
return Expr{b.InlineCall(pkg.rtFunc("MakeAnyInt"), t, vptr).impl, tinter}
2024-05-09 13:45:29 +08:00
case kind == types.Float32:
t := b.InlineCall(pkg.rtFunc("Basic"), prog.Val(int(kind)))
tptr := prog.Uintptr()
i32 := b.impl.CreateBitCast(x.impl, prog.tyInt32(), "")
vptr := Expr{llvm.CreateIntCast(b.impl, i32, tptr.ll), tptr}
return Expr{b.InlineCall(pkg.rtFunc("MakeAnyInt"), t, vptr).impl, tinter}
case kind == types.Float64:
t := b.InlineCall(pkg.rtFunc("Basic"), prog.Val(int(kind)))
tptr := prog.Uintptr()
vptr := Expr{b.impl.CreateBitCast(x.impl, tptr.ll, ""), tptr}
return Expr{b.InlineCall(pkg.rtFunc("MakeAnyInt"), t, vptr).impl, tinter}
2024-05-09 06:48:16 +08:00
case kind == types.String:
return Expr{b.InlineCall(pkg.rtFunc("MakeAnyString"), x).impl, tinter}
2024-04-27 20:45:55 +08:00
}
}
2024-05-09 06:48:16 +08:00
panic("todo")
2024-04-27 20:45:55 +08:00
}
2024-04-27 17:39:25 +08:00
// The TypeAssert instruction tests whether interface value X has type
// AssertedType.
//
// If !CommaOk, on success it returns v, the result of the conversion
// (defined below); on failure it panics.
//
// If CommaOk: on success it returns a pair (v, true) where v is the
// result of the conversion; on failure it returns (z, false) where z
// is AssertedType's zero value. The components of the pair must be
// accessed using the Extract instruction.
//
// If Underlying: tests whether interface value X has the underlying
// type AssertedType.
//
// If AssertedType is a concrete type, TypeAssert checks whether the
// dynamic type in interface X is equal to it, and if so, the result
// of the conversion is a copy of the value in the interface.
//
// If AssertedType is an interface, TypeAssert checks whether the
// dynamic type of the interface is assignable to it, and if so, the
// result of the conversion is a copy of the interface value X.
// If AssertedType is a superinterface of X.Type(), the operation will
// fail iff the operand is nil. (Contrast with ChangeInterface, which
// performs no nil-check.)
//
// Type() reflects the actual type of the result, possibly a
// 2-types.Tuple; AssertedType is the asserted type.
//
// Depending on the TypeAssert's purpose, Pos may return:
// - the ast.CallExpr.Lparen of an explicit T(e) conversion;
// - the ast.TypeAssertExpr.Lparen of an explicit e.(T) operation;
// - the ast.CaseClause.Case of a case of a type-switch statement;
// - the Ident(m).NamePos of an interface method value i.m
// (for which TypeAssert may be used to effect the nil check).
//
// Example printed form:
//
// t1 = typeassert t0.(int)
// t3 = typeassert,ok t2.(T)
func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) {
if debugInstr {
2024-05-05 12:11:51 +08:00
log.Printf("TypeAssert %v, %v, %v\n", x.impl, assertedTyp.raw.Type, commaOk)
2024-04-27 17:39:25 +08:00
}
switch assertedTyp.kind {
2024-05-10 13:25:36 +08:00
case vkSigned, vkUnsigned, vkFloat, vkBool:
pkg := b.Func.Pkg
2024-04-27 17:39:25 +08:00
fnName := "I2Int"
if commaOk {
fnName = "CheckI2Int"
}
fn := pkg.rtFunc(fnName)
var kind types.BasicKind
2024-05-05 12:11:51 +08:00
switch t := assertedTyp.raw.Type.(type) {
2024-04-27 17:39:25 +08:00
case *types.Basic:
kind = t.Kind()
default:
panic("todo")
}
2024-04-29 13:59:06 +08:00
typ := b.InlineCall(pkg.rtFunc("Basic"), b.Prog.Val(int(kind)))
2024-05-10 13:25:36 +08:00
ret = b.InlineCall(fn, x, typ)
if kind != types.Uintptr {
conv := func(v llvm.Value) llvm.Value {
switch kind {
case types.Float32:
v = castInt(b.impl, v, b.Prog.tyInt32())
v = b.impl.CreateBitCast(v, assertedTyp.ll, "")
case types.Float64:
v = b.impl.CreateBitCast(v, assertedTyp.ll, "")
default:
v = castInt(b.impl, v, assertedTyp.ll)
}
return v
2024-05-09 13:45:29 +08:00
}
2024-05-10 13:25:36 +08:00
if !commaOk {
ret.Type = assertedTyp
ret.impl = conv(ret.impl)
} else {
ret.Type = b.Prog.toTuple(
types.NewTuple(
types.NewVar(token.NoPos, nil, "", assertedTyp.RawType()),
ret.Type.RawType().(*types.Tuple).At(1),
),
)
val0 := conv(b.impl.CreateExtractValue(ret.impl, 0, ""))
val1 := b.impl.CreateExtractValue(ret.impl, 1, "")
ret.impl = llvm.ConstStruct([]llvm.Value{val0, val1}, false)
2024-05-09 13:45:29 +08:00
}
}
2024-05-10 13:25:36 +08:00
return
case vkString:
2024-05-09 13:45:29 +08:00
pkg := b.Func.Pkg
2024-05-10 13:25:36 +08:00
fnName := "I2String"
if commaOk {
fnName = "CheckI2String"
}
2024-05-09 13:45:29 +08:00
fn := pkg.rtFunc(fnName)
2024-05-10 13:25:36 +08:00
typ := b.InlineCall(pkg.rtFunc("Basic"), b.Prog.Val(int(abi.String)))
2024-05-09 13:45:29 +08:00
return b.InlineCall(fn, x, typ)
2024-04-27 17:39:25 +08:00
}
panic("todo")
}
2024-04-18 22:18:43 +08:00
// -----------------------------------------------------------------------------
2024-04-28 06:23:21 +08:00
// TODO(xsw): make inline call
2024-04-27 17:39:25 +08:00
func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) {
return b.Call(fn, args...)
}
2024-04-21 15:12:57 +08:00
// The Call instruction represents a function or method call.
//
// The Call instruction yields the function result if there is exactly
// one. Otherwise it returns a tuple, the components of which are
// accessed via Extract.
//
// Example printed form:
//
// t2 = println(t0, t1)
// t4 = t3()
// t7 = invoke t5.Println(...t6)
2024-04-19 00:05:57 +08:00
func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
2024-04-21 15:12:57 +08:00
if debugInstr {
var b bytes.Buffer
2024-05-04 07:21:07 +08:00
name := fn.impl.Name()
if name == "" {
name = "closure"
}
2024-05-05 13:29:20 +08:00
fmt.Fprint(&b, "Call ", fn.kind, " ", fn.raw.Type, " ", name)
2024-05-04 07:21:07 +08:00
sep := ": "
2024-04-21 15:12:57 +08:00
for _, arg := range args {
2024-05-04 07:21:07 +08:00
fmt.Fprint(&b, sep, arg.impl)
sep = ", "
2024-04-21 15:12:57 +08:00
}
log.Println(b.String())
}
2024-05-11 23:38:21 +08:00
var kind = fn.kind
2024-05-12 23:08:44 +08:00
if kind == vkPyFuncRef {
2024-05-11 23:38:21 +08:00
return b.pyCall(fn, args)
}
var ll llvm.Type
2024-05-05 18:20:51 +08:00
var data Expr
var sig *types.Signature
2024-05-11 23:38:21 +08:00
var prog = b.Prog
2024-05-05 12:11:51 +08:00
var raw = fn.raw.Type
2024-05-11 23:38:21 +08:00
switch kind {
2024-05-03 23:49:52 +08:00
case vkClosure:
2024-05-05 18:20:51 +08:00
data = b.Field(fn, 1)
fn = b.Field(fn, 0)
2024-05-05 12:11:51 +08:00
raw = fn.raw.Type
2024-05-04 07:21:07 +08:00
fallthrough
case vkFuncPtr:
2024-05-05 12:11:51 +08:00
sig = raw.(*types.Signature)
ll = prog.FuncDecl(sig, InC).ll
case vkFuncDecl:
sig = raw.(*types.Signature)
ll = fn.ll
2024-04-19 00:05:57 +08:00
default:
2024-05-04 07:47:18 +08:00
panic("unreachable")
2024-04-18 15:03:10 +08:00
}
ret.Type = prog.retType(sig)
2024-05-05 18:20:51 +08:00
ret.impl = llvm.CreateCall(b.impl, ll, fn.impl, llvmParamsEx(data, args, sig.Params(), b))
2024-04-19 00:05:57 +08:00
return
2024-04-18 15:03:10 +08:00
}
2024-04-18 22:18:43 +08:00
2024-05-06 19:42:18 +08:00
// The Extract instruction yields component Index of Tuple.
//
// This is used to access the results of instructions with multiple
// return values, such as Call, TypeAssert, Next, UnOp(ARROW) and
// IndexExpr(Map).
//
// Example printed form:
//
// t1 = extract t0 #1
func (b Builder) Extract(x Expr, index int) (ret Expr) {
ret.Type = b.Prog.toType(x.Type.raw.Type.(*types.Tuple).At(index).Type())
ret.impl = b.impl.CreateExtractValue(x.impl, index, "")
return
}
2024-04-26 00:31:02 +08:00
// A Builtin represents a specific use of a built-in function, e.g. len.
//
// Builtins are immutable values. Builtins do not have addresses.
//
// `fn` indicates the function: one of the built-in functions from the
// Go spec (excluding "make" and "new").
func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) {
2024-04-30 08:23:55 +08:00
switch fn {
case "len":
if len(args) == 1 {
arg := args[0]
2024-05-15 10:29:06 +08:00
switch arg.kind {
case vkSlice:
return b.SliceLen(arg)
case vkString:
return b.StringLen(arg)
2024-05-02 15:49:32 +08:00
}
}
case "cap":
if len(args) == 1 {
arg := args[0]
2024-05-15 10:29:06 +08:00
switch arg.kind {
case vkSlice:
return b.SliceCap(arg)
}
}
case "append":
if len(args) == 2 {
src := args[0]
if src.kind == vkSlice {
elem := args[1]
switch elem.kind {
case vkSlice:
etSize := b.Prog.SizeOf(b.Prog.Elem(elem.Type))
ret.Type = src.Type
ret.impl = b.InlineCall(b.Func.Pkg.rtFunc("SliceAppend"),
src, b.SliceData(elem), b.SliceLen(elem), b.Prog.Val(int(etSize))).impl
return
case vkString:
etSize := b.Prog.SizeOf(b.Prog.Type(types.Typ[types.Byte], InGo))
ret.Type = src.Type
ret.impl = b.InlineCall(b.Func.Pkg.rtFunc("SliceAppend"),
src, b.StringData(elem), b.StringLen(elem), b.Prog.Val(int(etSize))).impl
return
}
2024-04-30 08:23:55 +08:00
}
}
}
2024-04-26 00:31:02 +08:00
panic("todo")
}
2024-04-18 22:18:43 +08:00
// -----------------------------------------------------------------------------