Files
llgo/ssa/operator.go
2024-04-18 01:18:41 +08:00

179 lines
4.6 KiB
Go

/*
* 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
import (
"go/token"
"github.com/goplus/llvm"
)
type valueKind = int
type Value struct {
impl llvm.Value
kind valueKind
}
const (
mathOpBase = token.ADD
mathOpLast = token.REM
)
const (
vkInvalid valueKind = iota
vkSigned
vkUnsigned
vkFloat
vkComplex
vkString
vkBool
)
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,
token.SHR - logicOpBase: llvm.LShr,
}
// 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,
}
// EQL NEQ LSS LEQ GTR GEQ == != < <= < >=
func isPredOp(op token.Token) bool {
return op >= predOpBase && op <= predOpLast
}
// op:
// 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 Value) (v Value) {
switch {
case isMathOp(op): // op: + - * / %
switch x.kind {
case vkString, vkComplex:
panic("todo")
}
idx := mathOpIdx(op, x.kind)
if llop := mathOpToLLVM[idx]; llop != 0 {
v.impl = llvm.CreateBinOp(b.impl, llop, x.impl, y.impl)
return
}
case isLogicOp(op): // op: & | ^ << >> &^
if op == token.AND_NOT {
panic("todo")
}
llop := logicOpToLLVM[op-logicOpBase]
if op == token.SHR && x.kind == vkUnsigned {
llop = llvm.AShr
}
v.impl = llvm.CreateBinOp(b.impl, llop, x.impl, y.impl)
return
case isPredOp(op): // op: == != < <= < >=
switch x.kind {
case vkSigned:
pred := intPredOpToLLVM[op-predOpBase]
v.impl = llvm.CreateICmp(b.impl, pred, x.impl, y.impl)
return
case vkUnsigned:
pred := uintPredOpToLLVM[op-predOpBase]
v.impl = llvm.CreateICmp(b.impl, pred, x.impl, y.impl)
return
case vkFloat:
pred := floatPredOpToLLVM[op-predOpBase]
v.impl = llvm.ConstFCmp(pred, x.impl, y.impl)
return
case vkString, vkComplex, vkBool:
panic("todo")
}
}
return
}