package llvm
import (
"fmt"
"math"
"strconv"
"strings"
"github.com/marzeq/qk/attributes"
"github.com/marzeq/qk/ir"
"github.com/marzeq/qk/symbols"
"github.com/marzeq/qk/types"
)
type Emitter struct {
Variables map[ir.SlotID]*symbols.Symbol
SlotTypes map[ir.SlotID]types.Type
ModuleName string
TargetTriple string
Executable bool
currentFn *ir.Function
externMap map[string]string // qk name -> actual symbol name for @foreign functions
stringMap map[stringLiteralKey]string
stringDefs []string
abiTemp int
sretParam string
}
type stringLiteralKey struct {
value string
nullTerminated bool
}
func (e *Emitter) EmitModule(out *strings.Builder, m *ir.Module) {
if e.ModuleName != "" {
fmt.Fprintf(out, "; module %s\n\n", e.ModuleName)
}
e.externMap = make(map[string]string)
e.stringMap = make(map[stringLiteralKey]string)
e.stringDefs = e.collectStringDefs(m)
e.abiTemp = 0
for _, def := range e.stringDefs {
out.WriteString(def)
out.WriteString("\n")
}
for _, global := range m.Globals {
e.GlobalEmit(out, global)
out.WriteString("\n")
}
for _, global := range m.ExternGlobals {
e.ExternGlobalEmit(out, global)
out.WriteString("\n")
}
if m.Initializer != "" && !e.Executable {
fmt.Fprintf(out, "@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @%s, ptr null }]\n\n", m.Initializer)
}
if len(e.stringDefs) > 0 || len(m.Globals) > 0 || len(m.ExternGlobals) > 0 {
out.WriteString("\n")
}
for i, ex := range m.Externs {
if i > 0 {
out.WriteString("\n")
}
llvmName := ex.Name
if ex.From != "" {
llvmName = ex.From
fmt.Fprintf(out, "; extern from %s\n", ex.From)
}
e.externMap[ex.Name] = llvmName
foreign := attributes.UsesCABI(ex.Signature.Attributes)
returnType := e.TypeEmit(ex.Signature.ReturnType)
if foreign {
returnType = e.foreignABIReturnType(ex.Signature.ReturnType)
}
fmt.Fprintf(out, "declare %s%s @%s(", e.visibilityEmit(ex.Visibility), returnType, llvmName)
writtenParams := 0
if foreign && e.foreignABIReturnUsesSRet(ex.Signature.ReturnType) {
out.WriteString(e.foreignABISRetArgument(ex.Signature.ReturnType, ""))
writtenParams++
}
for _, p := range ex.Signature.ParamTypes {
abiTypes := []string{e.TypeEmit(p)}
if foreign {
abiTypes = e.foreignABIParamTypes(p)
}
for _, abiType := range abiTypes {
if writtenParams > 0 {
out.WriteString(", ")
}
out.WriteString(abiType)
writtenParams++
}
}
if ex.Signature.Variadic {
if writtenParams > 0 {
out.WriteString(", ")
}
out.WriteString("...")
}
out.WriteString(")")
for _, attr := range ex.Signature.Attributes {
at := attr.GetType()
switch at {
case attributes.AttributeTypeNoReturn:
out.WriteString(" noreturn")
case attributes.AttributeTypeNoInline:
out.WriteString(" noinline")
case attributes.AttributeTypeInline:
out.WriteString(" alwaysinline")
case attributes.AttributeTypeExport:
case attributes.AttributeTypeForeign:
default:
panic(fmt.Sprintf("unsupported attribute type for extern function: %s", at))
}
}
out.WriteString("\n")
}
for i, fn := range m.Functions {
if i > 0 {
out.WriteString("\n")
}
e.EmitFunction(out, fn)
if !strings.HasSuffix(out.String(), "\n") {
out.WriteString("\n")
}
}
}
func (e *Emitter) ExternGlobalEmit(out *strings.Builder, global ir.ExternGlobal) {
// An immutable source global may still be assigned by another module's
// runtime initializer before program entry, so importing declarations must
// not promise LLVM that the storage is constant.
fmt.Fprintf(out, "@%s = external %sglobal %s", global.Name, e.visibilityEmit(global.Visibility), e.TypeEmit(global.Type))
}
func (e *Emitter) GlobalEmit(out *strings.Builder, global ir.Global) {
kind := "constant"
if global.Mutable {
kind = "global"
}
linkage := e.linkageEmit(global.Linkage)
visibility := e.visibilityEmit(global.Visibility)
fmt.Fprintf(out, "@%s = %s%s%s %s %s", global.Name, linkage, visibility, kind, e.TypeEmit(global.Type), e.OperandEmit(global.Value))
}
func (e *Emitter) EmitFunction(out *strings.Builder, fn *ir.Function) {
e.currentFn = fn
e.sretParam = ""
returnType := fn.Signature.ReturnType
cABI := attributes.UsesCABI(fn.Attributes)
returnTypeText := e.TypeEmit(returnType)
if cABI {
returnTypeText = e.foreignABIReturnType(returnType)
}
linkage := e.linkageEmit(fn.Linkage)
visibility := e.visibilityEmit(fn.Visibility)
fmt.Fprintf(out, "define %s%s%s @%s(", linkage, visibility, returnTypeText, fn.Name)
paramTypes := fn.Signature.ParamTypes
if len(paramTypes) == 0 && len(fn.Parameters) > 0 {
paramTypes = make([]types.Type, len(fn.Parameters))
for i, param := range fn.Parameters {
paramTypes[i] = param.Type
}
}
writtenParams := 0
if cABI && e.foreignABIReturnUsesSRet(returnType) {
e.sretParam = e.availableSRetParamName(fn)
out.WriteString(e.foreignABISRetArgument(returnType, "%"+e.sretParam))
writtenParams++
}
for i, paramType := range paramTypes {
paramName := fmt.Sprintf("arg%d", i)
if i < len(fn.Parameters) && fn.Parameters[i].Name != "" {
paramName = fn.Parameters[i].Name
}
abiTypes := e.callABIParamTypes(paramType, cABI)
for j, abiType := range abiTypes {
if writtenParams > 0 {
out.WriteString(", ")
}
name := paramName
if len(abiTypes) > 1 || (cABI && len(e.foreignABIChunks(paramType)) > 0) {
name = fmt.Sprintf("%s.abi%d", paramName, j)
}
fmt.Fprintf(out, "%s %%%s", abiType, name)
writtenParams++
}
}
if fn.Signature.Variadic {
if writtenParams > 0 {
out.WriteString(", ")
}
out.WriteString("...")
}
out.WriteString(") ")
for _, attr := range fn.Attributes {
at := attr.GetType()
switch at {
case attributes.AttributeTypeNoInline:
out.WriteString("noinline ")
case attributes.AttributeTypeInline:
out.WriteString("alwaysinline ")
case attributes.AttributeTypeNoReturn:
out.WriteString("noreturn ")
case attributes.AttributeTypeExport:
default:
panic(fmt.Sprintf("unsupported attribute type for function: %s", at))
}
}
out.WriteString("{\n")
for _, slot := range fn.Slots {
fmt.Fprintf(out, " ; slot %s %s %s\n", e.SlotIDEmit(slot.ID), e.TypeEmit(slot.Type), slot.Name)
}
if len(fn.Slots) > 0 {
out.WriteString("\n")
}
for _, block := range fn.Blocks {
e.EmitBlock(out, block)
}
out.WriteString("}\n")
e.sretParam = ""
}
func (e *Emitter) availableSRetParamName(fn *ir.Function) string {
name := "__qk_sret"
for {
available := true
for _, param := range fn.Parameters {
if param.Name == name {
available = false
name = "_" + name
break
}
}
if available {
return name
}
}
}
func (e *Emitter) linkageEmit(linkage ir.Linkage) string {
if linkage == ir.LinkageInternal {
return "internal "
}
return ""
}
func (e *Emitter) visibilityEmit(visibility ir.Visibility) string {
if visibility == ir.VisibilityHidden {
return "hidden "
}
return ""
}
func (e *Emitter) EmitBlock(out *strings.Builder, block *ir.Block) {
label := e.blockLabel(block.ID, block.Name)
fmt.Fprintf(out, "%s:\n", label)
if e.currentFn != nil && block.ID == e.currentFn.Entry && attributes.UsesCABI(e.currentFn.Attributes) {
e.emitCABIParameterPrologue(out)
}
for _, instr := range block.Instr {
e.InstrEmit(out, instr)
out.WriteString("\n")
}
}
func (e *Emitter) emitCABIParameterPrologue(out *strings.Builder) {
for i, param := range e.currentFn.Parameters {
chunks := e.foreignABIChunks(param.Type)
if len(chunks) == 0 {
continue
}
name := param.Name
if name == "" {
name = fmt.Sprintf("arg%d", i)
}
if len(chunks) == 1 && chunks[0].offset == -1 {
fmt.Fprintf(out, " %%%s = load %s, ptr %%%s.abi0\n", name, e.TypeEmit(param.Type), name)
continue
}
allocationType, _ := e.abiScratchAllocation(param.Type, chunks)
slot := e.nextABITemp()
fmt.Fprintf(out, " %s = alloca %s\n", slot, allocationType)
for j, chunk := range chunks {
ptr := slot
if chunk.offset != 0 {
ptr = e.nextABITemp()
fmt.Fprintf(out, " %s = getelementptr i8, ptr %s, i64 %d\n", ptr, slot, chunk.offset)
}
fmt.Fprintf(out, " store %s %%%s.abi%d, ptr %s, align 1\n", chunk.typeName, name, j, ptr)
}
fmt.Fprintf(out, " %%%s = load %s, ptr %s\n", name, e.TypeEmit(param.Type), slot)
}
}
func (e *Emitter) slotType(slot ir.SlotID) types.Type {
fn := e.currentFn
if fn == nil {
panic("no current function set")
}
if ty := e.SlotTypes[slot]; ty != nil {
return ty
}
for _, declared := range fn.Slots {
if declared.ID == slot {
return declared.Type
}
}
if symbol, ok := e.Variables[slot]; ok && symbol != nil {
return symbol.Type
}
panic("unknown slot type")
}
func (e *Emitter) pointerBaseType(ty types.Type) types.Type {
ty = types.Underlying(ty)
if ptr, ok := ty.(types.PointerType); ok {
return ptr.Base
}
panic("expected pointer type")
}
func (e *Emitter) structFieldIndex(ty types.Type, field string) int {
ty = types.Underlying(ty)
if _, ok := ty.(types.SliceType); ok {
switch field {
case "0":
return 0
case "1":
return 1
default:
panic("slice field index not found")
}
}
st, ok := ty.(types.StructType)
if !ok {
panic(fmt.Sprintf("expected struct type, got %T (%v) for field %q", ty, ty, field))
}
for i, entry := range st.Fields {
if entry.L == field {
return i
}
if entry.L == "" {
if embedded, ok := entry.R.(types.UnionType); ok {
for _, unionField := range embedded.Fields {
if unionField.L == field {
return i
}
}
}
}
}
panic("field not found")
}
func (e *Emitter) TypeEmit(ty types.Type) string {
ty = types.Underlying(ty)
switch ty := ty.(type) {
case types.PrimitiveType:
switch ty {
case types.PrimitiveBool:
return "i1"
case types.PrimitiveVoid:
return "void"
case types.PrimitiveIsz:
return e.pointerIntType()
case types.PrimitiveUsz:
return e.pointerIntType()
case types.PrimitiveF32:
return "float"
case types.PrimitiveF64:
return "double"
case types.PrimitiveI8:
return "i8"
case types.PrimitiveI16:
return "i16"
case types.PrimitiveI32:
return "i32"
case types.PrimitiveI64:
return "i64"
case types.PrimitiveU8:
return "i8"
case types.PrimitiveU16:
return "i16"
case types.PrimitiveU32:
return "i32"
case types.PrimitiveU64:
return "i64"
default:
return ty.String()
}
case types.PointerType:
return "ptr"
case types.TraitPointerType:
return "{ ptr, ptr }"
case types.EnumType:
return "i32"
case types.FlagsType:
return e.TypeEmit(ty.Underlying)
case types.StructType:
var sb strings.Builder
if ty.Packed {
sb.WriteString("<{ ")
} else {
sb.WriteString("{ ")
}
for i, field := range ty.Fields {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString(e.TypeEmit(field.R))
}
if ty.Packed {
sb.WriteString(" }>")
} else {
sb.WriteString(" }")
}
return sb.String()
case types.MultipleReturnType:
var sb strings.Builder
sb.WriteString("{ ")
for i, item := range ty.Types {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString(e.TypeEmit(item))
}
sb.WriteString(" }")
return sb.String()
case types.UnionType:
size, align := e.typeSizeAlign(ty)
return fmt.Sprintf("[%d x i%d]", size/align, align*8)
case types.SliceType:
return fmt.Sprintf("{ ptr, %s }", e.pointerIntType())
case types.ArrayType:
return fmt.Sprintf("[%d x %s]", ty.Length, e.TypeEmit(ty.Base))
case types.FunctionType:
var sb strings.Builder
sb.WriteString(e.TypeEmit(ty.ReturnType))
sb.WriteString(" (")
for i, param := range ty.Parameters {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString(e.TypeEmit(param))
}
sb.WriteString(")")
return sb.String()
default:
panic(fmt.Sprintf("unsupported type: %T", ty))
}
}
func (e *Emitter) OperandEmit(op ir.Operand) string {
switch op.Kind {
case ir.OperandValue:
if name, ok := e.parameterNameForValue(op.Value); ok {
return "%" + name
}
return e.ValueIDEmit(op.Value)
case ir.OperandIntConst:
return op.IntValue
case ir.OperandFloatConst:
return llvmFloatLiteral(op.FloatValue, op.Type)
case ir.OperandBoolConst:
if op.BoolValue {
return "1"
}
return "0"
case ir.OperandNullConst:
return "null"
case ir.OperandStructConst:
var fields strings.Builder
fields.WriteString("{ ")
for i, field := range op.Fields {
if i > 0 {
fields.WriteString(", ")
}
fmt.Fprintf(&fields, "%s %s", e.TypeEmit(field.Type), e.OperandEmit(field))
}
fields.WriteString(" }")
return fields.String()
case ir.OperandCStringConst:
key := stringLiteralKey{value: op.StringValue, nullTerminated: true}
global, ok := e.stringMap[key]
if !ok {
panic("missing C string constant definition")
}
length := len(op.StringValue) + 1
return fmt.Sprintf("getelementptr inbounds ([%d x i8], ptr %s, i64 0, i64 0)", length, global)
case ir.OperandFunctionConst:
name := op.FunctionName
if mapped, ok := e.externMap[name]; ok && mapped != "" {
name = mapped
}
return "@" + name
case ir.OperandZeroConst:
return "zeroinitializer"
case ir.OperandSizeofConst:
return fmt.Sprintf(
"ptrtoint (ptr getelementptr (%s, ptr null, i32 1) to %s)",
e.TypeEmit(op.SubjectType), e.TypeEmit(op.Type),
)
case ir.OperandBinaryConst:
return fmt.Sprintf(
"%s (%s %s, %s %s)",
op.Operator, e.TypeEmit(op.Left.Type), e.OperandEmit(*op.Left),
e.TypeEmit(op.Right.Type), e.OperandEmit(*op.Right),
)
default:
panic("unreachable")
}
}
func llvmFloatLiteral(value string, ty types.Type) string {
floatValue, err := strconv.ParseFloat(value, 64)
if err != nil {
panic(fmt.Sprintf("invalid float literal %q: %v", value, err))
}
if ty.Equals(types.PrimitiveF32) {
floatValue = float64(float32(floatValue))
}
return fmt.Sprintf("0x%016X", math.Float64bits(floatValue))
}
func (e *Emitter) parameterNameForValue(id ir.ValueID) (string, bool) {
if e.currentFn == nil {
return "", false
}
index := int(id) - 1
if index < 0 || index >= len(e.currentFn.Parameters) {
return "", false
}
param := e.currentFn.Parameters[index]
if param.Name != "" {
return param.Name, true
}
return fmt.Sprintf("arg%d", index), true
}
func (e *Emitter) ValueIDEmit(id ir.ValueID) string {
return fmt.Sprintf("%%v%d", id)
}
func (e *Emitter) SlotIDEmit(id ir.SlotID) string {
return fmt.Sprintf("%%s%d", id)
}
func (e *Emitter) BlockIDEmit(id ir.BlockID) string {
return "%" + e.blockLabel(id, "")
}
func (e *Emitter) blockLabel(id ir.BlockID, fallbackName string) string {
if e.currentFn != nil {
for _, block := range e.currentFn.Blocks {
if block.ID != id {
continue
}
if block.Name != "" {
return fmt.Sprintf("%s.%d", block.Name, id)
}
break
}
}
if fallbackName != "" {
return fmt.Sprintf("%s.%d", fallbackName, id)
}
return fmt.Sprintf("b%d", id)
}
func (e *Emitter) InstrEmit(out *strings.Builder, instr ir.Instr) {
switch instr := instr.(type) {
case ir.Add:
e.AddEmit(out, instr)
case ir.Sub:
e.SubEmit(out, instr)
case ir.Mul:
e.MulEmit(out, instr)
case ir.Div:
e.DivEmit(out, instr)
case ir.CmpEq:
e.CmpEqEmit(out, instr)
case ir.CmpNe:
e.CmpNeEmit(out, instr)
case ir.CmpLt:
e.CmpLtEmit(out, instr)
case ir.CmpGt:
e.CmpGtEmit(out, instr)
case ir.CmpLe:
e.CmpLeEmit(out, instr)
case ir.CmpGe:
e.CmpGeEmit(out, instr)
case ir.Mod:
e.ModEmit(out, instr)
case ir.LogicalOr:
e.LogicalOrEmit(out, instr)
case ir.LogicalAnd:
e.LogicalAndEmit(out, instr)
case ir.BitwiseAnd:
e.BitwiseAndEmit(out, instr)
case ir.BitwiseOr:
e.BitwiseOrEmit(out, instr)
case ir.BitwiseXor:
e.BitwiseXorEmit(out, instr)
case ir.ShiftLeft:
e.ShiftLeftEmit(out, instr)
case ir.ShiftRight:
e.ShiftRightEmit(out, instr)
case ir.Negate:
e.NegateEmit(out, instr)
case ir.LogicalNot:
e.LogicalNotEmit(out, instr)
case ir.BitwiseNot:
e.BitwiseNotEmit(out, instr)
case ir.Alloca:
e.AllocaEmit(out, instr)
case ir.AllocaArray:
e.AllocaArrayEmit(out, instr)
case ir.Load:
e.LoadEmit(out, instr)
case ir.LoadGlobal:
e.LoadGlobalEmit(out, instr)
case ir.Store:
e.StoreEmit(out, instr)
case ir.StoreGlobal:
e.StoreGlobalEmit(out, instr)
case ir.AddressOf:
e.AddressOfEmit(out, instr)
case ir.AddressOfGlobal:
e.AddressOfGlobalEmit(out, instr)
case ir.FieldAddress:
e.FieldAddressEmit(out, instr)
case ir.ElementAddress:
e.ElementAddressEmit(out, instr)
case ir.LoadPtr:
e.LoadPtrEmit(out, instr)
case ir.StorePtr:
e.StorePtrEmit(out, instr)
case ir.InsertValue:
fmt.Fprintf(out, "%s = insertvalue %s %s, %s %s, %d", e.ValueIDEmit(instr.Dest), e.TypeEmit(instr.Aggregate.Type), e.OperandEmit(instr.Aggregate), e.TypeEmit(instr.Value.Type), e.OperandEmit(instr.Value), instr.Index)
case ir.ExtractValue:
fmt.Fprintf(out, "%s = extractvalue %s %s, %d", e.ValueIDEmit(instr.Dest), e.TypeEmit(instr.Aggregate.Type), e.OperandEmit(instr.Aggregate), instr.Index)
case ir.Call:
e.CallEmit(out, instr)
case ir.InlineAsm:
e.InlineAsmEmit(out, instr)
case ir.Jump:
e.JumpEmit(out, instr)
case ir.Branch:
e.BranchEmit(out, instr)
case ir.Return:
e.ReturnEmit(out, instr)
case ir.Unreachable:
out.WriteString("unreachable")
case ir.Cast:
e.CastEmit(out, instr)
case ir.Sizeof:
e.SizeofEmit(out, instr)
case ir.Alignof:
e.AlignofEmit(out, instr)
case ir.Offsetof:
e.OffsetofEmit(out, instr)
case ir.StringConst:
e.StringConstEmit(out, instr)
default:
panic("unreachable")
}
}
func (e *Emitter) collectStringDefs(m *ir.Module) []string {
defs := []string{}
add := func(value string, nullTerminated bool) {
key := stringLiteralKey{value: value, nullTerminated: nullTerminated}
if _, exists := e.stringMap[key]; exists {
return
}
global := fmt.Sprintf("@.str.%d", len(e.stringMap))
e.stringMap[key] = global
encoded := encodeLLVMString(value)
length := len(value)
if nullTerminated {
encoded += "\\00"
length++
}
defs = append(defs, fmt.Sprintf("%s = private unnamed_addr constant [%d x i8] c\"%s\", align 1", global, length, encoded))
}
for _, fn := range m.Functions {
for _, block := range fn.Blocks {
for _, instr := range block.Instr {
s, ok := instr.(ir.StringConst)
if !ok {
continue
}
add(s.Value, s.NullTerminated)
}
}
}
for _, global := range m.Globals {
if global.Value.Kind == ir.OperandCStringConst {
add(global.Value.StringValue, true)
}
}
return defs
}
func encodeLLVMString(value string) string {
var b strings.Builder
for i := 0; i < len(value); i++ {
fmt.Fprintf(&b, "\\%02X", value[i])
}
return b.String()
}
func (e *Emitter) StringConstEmit(out *strings.Builder, s ir.StringConst) {
key := stringLiteralKey{value: s.Value, nullTerminated: s.NullTerminated}
global, ok := e.stringMap[key]
if !ok {
panic("missing string constant definition")
}
length := len(s.Value)
if s.NullTerminated {
length++
}
fmt.Fprintf(out, "%s = getelementptr inbounds [%d x i8], ptr %s, i64 0, i64 0", e.ValueIDEmit(s.Dest), length, global)
}
func (e *Emitter) AddEmit(out *strings.Builder, a ir.Add) {
instr := "add"
if types.IsFloat(a.Left.Type) {
instr = "fadd"
}
fmt.Fprintf(out, "%s = %s %s %s, %s", e.ValueIDEmit(a.Dest), instr, e.TypeEmit(a.Left.Type), e.OperandEmit(a.Left), e.OperandEmit(a.Right))
}
func (e *Emitter) SubEmit(out *strings.Builder, s ir.Sub) {
instr := "sub"
if types.IsFloat(s.Left.Type) {
instr = "fsub"
}
fmt.Fprintf(out, "%s = %s %s %s, %s", e.ValueIDEmit(s.Dest), instr, e.TypeEmit(s.Left.Type), e.OperandEmit(s.Left), e.OperandEmit(s.Right))
}
func (e *Emitter) MulEmit(out *strings.Builder, m ir.Mul) {
instr := "mul"
if types.IsFloat(m.Left.Type) {
instr = "fmul"
}
fmt.Fprintf(out, "%s = %s %s %s, %s", e.ValueIDEmit(m.Dest), instr, e.TypeEmit(m.Left.Type), e.OperandEmit(m.Left), e.OperandEmit(m.Right))
}
func (e *Emitter) DivEmit(out *strings.Builder, d ir.Div) {
instr := "sdiv"
if types.IsFloat(d.Left.Type) {
instr = "fdiv"
} else if types.IsUnsigned(d.Left.Type) {
instr = "udiv"
}
fmt.Fprintf(out, "%s = %s %s %s, %s", e.ValueIDEmit(d.Dest), instr, e.TypeEmit(d.Left.Type), e.OperandEmit(d.Left), e.OperandEmit(d.Right))
}
func (e *Emitter) ModEmit(out *strings.Builder, m ir.Mod) {
instr := "srem"
if types.IsFloat(m.Left.Type) {
instr = "frem"
} else if types.IsUnsigned(m.Left.Type) {
instr = "urem"
}
fmt.Fprintf(out, "%s = %s %s %s, %s", e.ValueIDEmit(m.Dest), instr, e.TypeEmit(m.Left.Type), e.OperandEmit(m.Left), e.OperandEmit(m.Right))
}
func (e *Emitter) CmpEqEmit(out *strings.Builder, c ir.CmpEq) {
instr := "icmp eq"
if types.IsFloat(c.Left.Type) {
instr = "fcmp oeq"
}
fmt.Fprintf(out, "%s = %s %s %s, %s", e.ValueIDEmit(c.Dest), instr, e.TypeEmit(c.Left.Type), e.OperandEmit(c.Left), e.OperandEmit(c.Right))
}
func (e *Emitter) CmpNeEmit(out *strings.Builder, c ir.CmpNe) {
instr := "icmp ne"
if types.IsFloat(c.Left.Type) {
instr = "fcmp une"
}
fmt.Fprintf(out, "%s = %s %s %s, %s", e.ValueIDEmit(c.Dest), instr, e.TypeEmit(c.Left.Type), e.OperandEmit(c.Left), e.OperandEmit(c.Right))
}
func (e *Emitter) CmpLtEmit(out *strings.Builder, c ir.CmpLt) {
instr := "icmp slt"
if types.IsFloat(c.Left.Type) {
instr = "fcmp olt"
} else if types.IsUnsigned(c.Left.Type) {
instr = "icmp ult"
}
fmt.Fprintf(out, "%s = %s %s %s, %s", e.ValueIDEmit(c.Dest), instr, e.TypeEmit(c.Left.Type), e.OperandEmit(c.Left), e.OperandEmit(c.Right))
}
func (e *Emitter) CmpGtEmit(out *strings.Builder, c ir.CmpGt) {
instr := "icmp sgt"
if types.IsFloat(c.Left.Type) {
instr = "fcmp ogt"
} else if types.IsUnsigned(c.Left.Type) {
instr = "icmp ugt"
}
fmt.Fprintf(out, "%s = %s %s %s, %s", e.ValueIDEmit(c.Dest), instr, e.TypeEmit(c.Left.Type), e.OperandEmit(c.Left), e.OperandEmit(c.Right))
}
func (e *Emitter) CmpLeEmit(out *strings.Builder, c ir.CmpLe) {
instr := "icmp sle"
if types.IsFloat(c.Left.Type) {
instr = "fcmp ole"
} else if types.IsUnsigned(c.Left.Type) {
instr = "icmp ule"
}
fmt.Fprintf(out, "%s = %s %s %s, %s", e.ValueIDEmit(c.Dest), instr, e.TypeEmit(c.Left.Type), e.OperandEmit(c.Left), e.OperandEmit(c.Right))
}
func (e *Emitter) CmpGeEmit(out *strings.Builder, c ir.CmpGe) {
instr := "icmp sge"
if types.IsFloat(c.Left.Type) {
instr = "fcmp oge"
} else if types.IsUnsigned(c.Left.Type) {
instr = "icmp uge"
}
fmt.Fprintf(out, "%s = %s %s %s, %s", e.ValueIDEmit(c.Dest), instr, e.TypeEmit(c.Left.Type), e.OperandEmit(c.Left), e.OperandEmit(c.Right))
}
func (e *Emitter) LogicalOrEmit(out *strings.Builder, l ir.LogicalOr) {
fmt.Fprintf(out, "%s = or i1 %s, %s", e.ValueIDEmit(l.Dest), e.OperandEmit(l.Left), e.OperandEmit(l.Right))
}
func (e *Emitter) LogicalAndEmit(out *strings.Builder, l ir.LogicalAnd) {
fmt.Fprintf(out, "%s = and i1 %s, %s", e.ValueIDEmit(l.Dest), e.OperandEmit(l.Left), e.OperandEmit(l.Right))
}
func (e *Emitter) BitwiseAndEmit(out *strings.Builder, b ir.BitwiseAnd) {
fmt.Fprintf(out, "%s = and %s %s, %s", e.ValueIDEmit(b.Dest), e.TypeEmit(b.Left.Type), e.OperandEmit(b.Left), e.OperandEmit(b.Right))
}
func (e *Emitter) BitwiseOrEmit(out *strings.Builder, b ir.BitwiseOr) {
fmt.Fprintf(out, "%s = or %s %s, %s", e.ValueIDEmit(b.Dest), e.TypeEmit(b.Left.Type), e.OperandEmit(b.Left), e.OperandEmit(b.Right))
}
func (e *Emitter) BitwiseXorEmit(out *strings.Builder, b ir.BitwiseXor) {
fmt.Fprintf(out, "%s = xor %s %s, %s", e.ValueIDEmit(b.Dest), e.TypeEmit(b.Left.Type), e.OperandEmit(b.Left), e.OperandEmit(b.Right))
}
func (e *Emitter) ShiftLeftEmit(out *strings.Builder, s ir.ShiftLeft) {
fmt.Fprintf(out, "%s = shl %s %s, %s", e.ValueIDEmit(s.Dest), e.TypeEmit(s.Left.Type), e.OperandEmit(s.Left), e.OperandEmit(s.Right))
}
func (e *Emitter) ShiftRightEmit(out *strings.Builder, s ir.ShiftRight) {
op := "ashr"
if types.IsUnsigned(s.Left.Type) {
op = "lshr"
}
fmt.Fprintf(out, "%s = %s %s %s, %s", e.ValueIDEmit(s.Dest), op, e.TypeEmit(s.Left.Type), e.OperandEmit(s.Left), e.OperandEmit(s.Right))
}
func (e *Emitter) NegateEmit(out *strings.Builder, n ir.Negate) {
if types.IsFloat(n.Operand.Type) {
fmt.Fprintf(out, "%s = fneg %s %s", e.ValueIDEmit(n.Dest), e.TypeEmit(n.Operand.Type), e.OperandEmit(n.Operand))
return
}
fmt.Fprintf(out, "%s = sub %s 0, %s", e.ValueIDEmit(n.Dest), e.TypeEmit(n.Operand.Type), e.OperandEmit(n.Operand))
}
func (e *Emitter) LogicalNotEmit(out *strings.Builder, n ir.LogicalNot) {
fmt.Fprintf(out, "%s = xor i1 %s, true", e.ValueIDEmit(n.Dest), e.OperandEmit(n.Operand))
}
func (e *Emitter) BitwiseNotEmit(out *strings.Builder, n ir.BitwiseNot) {
fmt.Fprintf(out, "%s = xor %s %s, -1", e.ValueIDEmit(n.Dest), e.TypeEmit(n.Operand.Type), e.OperandEmit(n.Operand))
}
func (e *Emitter) AllocaEmit(out *strings.Builder, a ir.Alloca) {
fmt.Fprintf(out, "%s = alloca %s", e.SlotIDEmit(a.Slot), e.TypeEmit(e.slotType(a.Slot)))
}
func (e *Emitter) AllocaArrayEmit(out *strings.Builder, a ir.AllocaArray) {
fmt.Fprintf(out, "%s = alloca %s, %s %s", e.ValueIDEmit(a.Dest), e.TypeEmit(a.Element), e.TypeEmit(a.Count.Type), e.OperandEmit(a.Count))
}
func (e *Emitter) LoadEmit(out *strings.Builder, l ir.Load) {
fmt.Fprintf(out, "%s = load %s, ptr %s", e.ValueIDEmit(l.Dest), e.TypeEmit(e.slotType(l.Slot)), e.SlotIDEmit(l.Slot))
}
func (e *Emitter) LoadGlobalEmit(out *strings.Builder, l ir.LoadGlobal) {
fmt.Fprintf(out, "%s = load %s, ptr @%s", e.ValueIDEmit(l.Dest), e.TypeEmit(l.Type), l.Name)
}
func (e *Emitter) StoreEmit(out *strings.Builder, s ir.Store) {
fmt.Fprintf(out, "store %s %s, ptr %s", e.TypeEmit(e.slotType(s.Slot)), e.OperandEmit(s.Value), e.SlotIDEmit(s.Slot))
}
func (e *Emitter) StoreGlobalEmit(out *strings.Builder, s ir.StoreGlobal) {
fmt.Fprintf(out, "store %s %s, ptr @%s", e.TypeEmit(s.Value.Type), e.OperandEmit(s.Value), s.Name)
}
func (e *Emitter) AddressOfEmit(out *strings.Builder, s ir.AddressOf) {
fmt.Fprintf(out, "%s = getelementptr inbounds %s, ptr %s, i32 0", e.ValueIDEmit(s.Dest), e.TypeEmit(e.slotType(s.Slot)), e.SlotIDEmit(s.Slot))
}
func (e *Emitter) AddressOfGlobalEmit(out *strings.Builder, s ir.AddressOfGlobal) {
fmt.Fprintf(out, "%s = getelementptr inbounds %s, ptr @%s, i32 0", e.ValueIDEmit(s.Dest), e.TypeEmit(s.Type), s.Name)
}
func (e *Emitter) LoadPtrEmit(out *strings.Builder, l ir.LoadPtr) {
fmt.Fprintf(out, "%s = load %s, ptr %s, align 1", e.ValueIDEmit(l.Dest), e.TypeEmit(e.pointerBaseType(l.Ptr.Type)), e.OperandEmit(l.Ptr))
}
func (e *Emitter) StorePtrEmit(out *strings.Builder, s ir.StorePtr) {
fmt.Fprintf(out, "store %s %s, ptr %s, align 1", e.TypeEmit(s.Value.Type), e.OperandEmit(s.Value), e.OperandEmit(s.Ptr))
}
func (e *Emitter) FieldAddressEmit(out *strings.Builder, f ir.FieldAddress) {
baseTy := types.Underlying(f.Base.Type)
if ptr, ok := baseTy.(types.PointerType); ok {
baseTy = types.Underlying(ptr.Base)
}
if _, ok := baseTy.(types.UnionType); ok {
fmt.Fprintf(out, "%s = getelementptr inbounds %s, ptr %s, i32 0", e.ValueIDEmit(f.Dest), e.TypeEmit(baseTy), e.OperandEmit(f.Base))
return
}
fieldIndex := e.structFieldIndex(baseTy, f.Field)
fmt.Fprintf(out, "%s = getelementptr inbounds %s, ptr %s, i32 0, i32 %d", e.ValueIDEmit(f.Dest), e.TypeEmit(baseTy), e.OperandEmit(f.Base), fieldIndex)
}
func (e *Emitter) ElementAddressEmit(out *strings.Builder, eaddr ir.ElementAddress) {
if eaddr.ArrayObject {
array := types.Underlying(types.Underlying(eaddr.Base.Type).(types.PointerType).Base).(types.ArrayType)
fmt.Fprintf(
out,
"%s = getelementptr inbounds %s, ptr %s, i32 0, %s %s",
e.ValueIDEmit(eaddr.Dest),
e.TypeEmit(array),
e.OperandEmit(eaddr.Base),
e.TypeEmit(eaddr.Index.Type),
e.OperandEmit(eaddr.Index),
)
return
}
fmt.Fprintf(
out,
"%s = getelementptr inbounds %s, ptr %s, %s %s",
e.ValueIDEmit(eaddr.Dest),
e.TypeEmit(eaddr.Element),
e.OperandEmit(eaddr.Base),
e.TypeEmit(eaddr.Index.Type),
e.OperandEmit(eaddr.Index),
)
}
func (e *Emitter) CallEmit(out *strings.Builder, c ir.Call) {
fnName := c.Name
if e.externMap != nil {
if mapped, ok := e.externMap[c.Name]; ok && mapped != "" {
fnName = mapped
}
}
callTarget := "@" + fnName
if c.Callee != nil {
callTarget = e.OperandEmit(*c.Callee)
}
foreign := attributes.UsesCABI(c.Signature.Attributes)
usesSRet := foreign && e.foreignABIReturnUsesSRet(c.Signature.ReturnType)
callType := e.TypeEmit(c.Signature.ReturnType)
if foreign {
callType = e.foreignABIReturnType(c.Signature.ReturnType)
}
if c.Signature.Variadic {
var params strings.Builder
writtenParams := 0
if usesSRet {
params.WriteString(e.foreignABISRetArgument(c.Signature.ReturnType, ""))
writtenParams++
}
for _, param := range c.Signature.ParamTypes {
for _, abiType := range e.callABIParamTypes(param, foreign) {
if writtenParams > 0 {
params.WriteString(", ")
}
params.WriteString(abiType)
writtenParams++
}
}
if writtenParams > 0 {
params.WriteString(", ")
}
params.WriteString("...")
callType += " (" + params.String() + ")"
}
returnChunks := []abiChunk(nil)
if foreign {
returnChunks = e.foreignABIReturnChunks(c.Signature.ReturnType)
}
var argPrelude strings.Builder
sretSlot := ""
if usesSRet {
sretSlot = e.nextABITemp()
fmt.Fprintf(&argPrelude, "%s = alloca %s\n ", sretSlot, e.TypeEmit(c.Signature.ReturnType))
}
loweredArgs := make([][]string, len(c.Args))
for i, arg := range c.Args {
loweredArgs[i] = e.lowerCallArgument(&argPrelude, arg, foreign)
}
if argPrelude.Len() > 0 {
out.WriteString(argPrelude.String())
}
callResult := ""
if !usesSRet && !c.Signature.ReturnType.Equals(types.PrimitiveVoid) && len(returnChunks) > 0 {
callResult = e.nextABITemp()
}
if c.Signature.ReturnType.Equals(types.PrimitiveVoid) || usesSRet {
out.WriteString("call ")
out.WriteString(callType)
out.WriteString(" ")
out.WriteString(callTarget)
out.WriteString("(")
} else if callResult != "" {
fmt.Fprintf(out, "%s = call %s %s(", callResult, callType, callTarget)
} else {
fmt.Fprintf(out, "%s = call %s %s(", e.ValueIDEmit(c.Dest), callType, callTarget)
}
writtenArgs := 0
if usesSRet {
out.WriteString(e.foreignABISRetArgument(c.Signature.ReturnType, sretSlot))
writtenArgs++
}
for i := range c.Args {
for _, lowered := range loweredArgs[i] {
if writtenArgs > 0 {
out.WriteString(", ")
}
out.WriteString(lowered)
writtenArgs++
}
}
out.WriteString(")")
if usesSRet {
fmt.Fprintf(out, "\n %s = load %s, ptr %s", e.ValueIDEmit(c.Dest), e.TypeEmit(c.Signature.ReturnType), sretSlot)
} else if callResult != "" {
e.unpackForeignReturn(out, c, callResult, returnChunks)
}
noreturnAttr := c.Signature.Attributes.Get(attributes.AttributeTypeNoReturn)
if noreturnAttr != nil {
out.WriteString("\nunreachable")
}
}
func (e *Emitter) InlineAsmEmit(out *strings.Builder, asm ir.InlineAsm) {
constraints := append([]string(nil), asm.Constraints...)
for _, clobber := range asm.Clobbers {
constraints = append(constraints, "~{"+clobber+"}")
}
if !asm.ResultType.Equals(types.PrimitiveVoid) {
fmt.Fprintf(out, "%s = ", e.ValueIDEmit(asm.Dest))
}
fmt.Fprintf(out, "call %s asm", e.TypeEmit(asm.ResultType))
if asm.SideEffect {
out.WriteString(" sideeffect")
}
fmt.Fprintf(out, " \"%s\", \"%s\"(", encodeLLVMString(asm.Template), encodeLLVMString(strings.Join(constraints, ",")))
for i, arg := range asm.Args {
if i > 0 {
out.WriteString(", ")
}
fmt.Fprintf(out, "%s %s", e.TypeEmit(arg.Type), e.OperandEmit(arg))
}
out.WriteString(")")
}
func (e *Emitter) callABIParamTypes(ty types.Type, foreign bool) []string {
if !foreign {
return []string{e.TypeEmit(ty)}
}
return e.foreignABIParamTypes(ty)
}
func (e *Emitter) nextABITemp() string {
e.abiTemp++
return fmt.Sprintf("%%abi%d", e.abiTemp)
}
func (e *Emitter) lowerCallArgument(out *strings.Builder, arg ir.Operand, foreign bool) []string {
if !foreign {
return []string{fmt.Sprintf("%s %s", e.TypeEmit(arg.Type), e.OperandEmit(arg))}
}
chunks := e.foreignABIChunks(arg.Type)
if len(chunks) == 0 {
return []string{fmt.Sprintf("%s %s", e.TypeEmit(arg.Type), e.OperandEmit(arg))}
}
slot := e.nextABITemp()
allocationType, zeroInitialize := e.abiScratchAllocation(arg.Type, chunks)
fmt.Fprintf(out, "%s = alloca %s\n ", slot, allocationType)
if zeroInitialize {
fmt.Fprintf(out, "store %s zeroinitializer, ptr %s\n ", allocationType, slot)
}
fmt.Fprintf(out, "store %s %s, ptr %s\n ", e.TypeEmit(arg.Type), e.OperandEmit(arg), slot)
if len(chunks) == 1 && chunks[0].offset == -1 {
return []string{fmt.Sprintf("%s%s %s", chunks[0].typeName, chunks[0].attributes, slot)}
}
result := make([]string, len(chunks))
for i, chunk := range chunks {
ptr := slot
if chunk.offset != 0 {
ptr = e.nextABITemp()
fmt.Fprintf(out, "%s = getelementptr i8, ptr %s, i64 %d\n ", ptr, slot, chunk.offset)
}
value := e.nextABITemp()
fmt.Fprintf(out, "%s = load %s, ptr %s, align 1\n ", value, chunk.typeName, ptr)
result[i] = fmt.Sprintf("%s%s %s", chunk.typeName, chunk.attributes, value)
}
return result
}
func (e *Emitter) unpackForeignReturn(out *strings.Builder, c ir.Call, callResult string, chunks []abiChunk) {
slot := e.nextABITemp()
allocationType, _ := e.abiScratchAllocation(c.Signature.ReturnType, chunks)
fmt.Fprintf(out, "\n %s = alloca %s", slot, allocationType)
for i, chunk := range chunks {
value := callResult
if len(chunks) > 1 {
value = e.nextABITemp()
fmt.Fprintf(out, "\n %s = extractvalue %s %s, %d", value, e.foreignABIReturnType(c.Signature.ReturnType), callResult, i)
}
ptr := slot
if chunk.offset != 0 {
ptr = e.nextABITemp()
fmt.Fprintf(out, "\n %s = getelementptr i8, ptr %s, i64 %d", ptr, slot, chunk.offset)
}
fmt.Fprintf(out, "\n store %s %s, ptr %s, align 1", chunk.typeName, value, ptr)
}
fmt.Fprintf(out, "\n %s = load %s, ptr %s", e.ValueIDEmit(c.Dest), e.TypeEmit(c.Signature.ReturnType), slot)
}
func (e *Emitter) JumpEmit(out *strings.Builder, j ir.Jump) {
fmt.Fprintf(out, "br label %s", e.BlockIDEmit(j.Target))
}
func (e *Emitter) BranchEmit(out *strings.Builder, b ir.Branch) {
fmt.Fprintf(out, "br i1 %s, label %s, label %s", e.OperandEmit(b.Cond), e.BlockIDEmit(b.Then), e.BlockIDEmit(b.Else))
}
func (e *Emitter) ReturnEmit(out *strings.Builder, r ir.Return) {
if r.HasValue {
if attributes.UsesCABI(e.currentFn.Attributes) {
e.emitCABIReturn(out, r.Value)
return
}
fmt.Fprintf(out, "ret %s %s", e.TypeEmit(r.Value.Type), e.OperandEmit(r.Value))
return
}
out.WriteString("ret void")
}
func (e *Emitter) emitCABIReturn(out *strings.Builder, value ir.Operand) {
if e.foreignABIReturnUsesSRet(value.Type) {
fmt.Fprintf(out, "store %s %s, ptr %%%s\n ret void", e.TypeEmit(value.Type), e.OperandEmit(value), e.sretParam)
return
}
chunks := e.foreignABIReturnChunks(value.Type)
if len(chunks) == 0 {
fmt.Fprintf(out, "ret %s %s", e.TypeEmit(value.Type), e.OperandEmit(value))
return
}
slot := e.nextABITemp()
allocationType, zeroInitialize := e.abiScratchAllocation(value.Type, chunks)
fmt.Fprintf(out, "%s = alloca %s", slot, allocationType)
if zeroInitialize {
fmt.Fprintf(out, "\n store %s zeroinitializer, ptr %s", allocationType, slot)
}
fmt.Fprintf(out, "\n store %s %s, ptr %s", e.TypeEmit(value.Type), e.OperandEmit(value), slot)
values := make([]string, len(chunks))
for i, chunk := range chunks {
ptr := slot
if chunk.offset != 0 {
ptr = e.nextABITemp()
fmt.Fprintf(out, "\n %s = getelementptr i8, ptr %s, i64 %d", ptr, slot, chunk.offset)
}
values[i] = e.nextABITemp()
fmt.Fprintf(out, "\n %s = load %s, ptr %s, align 1", values[i], chunk.typeName, ptr)
}
if len(values) == 1 {
fmt.Fprintf(out, "\n ret %s %s", chunks[0].typeName, values[0])
return
}
retType := e.foreignABIReturnType(value.Type)
first := e.nextABITemp()
second := e.nextABITemp()
fmt.Fprintf(out, "\n %s = insertvalue %s undef, %s %s, 0", first, retType, chunks[0].typeName, values[0])
fmt.Fprintf(out, "\n %s = insertvalue %s %s, %s %s, 1", second, retType, first, chunks[1].typeName, values[1])
fmt.Fprintf(out, "\n ret %s %s", retType, second)
}
func (e *Emitter) CastEmit(out *strings.Builder, c ir.Cast) {
from := c.From.Type
to := c.To
fromRep := types.Underlying(from)
toRep := types.Underlying(to)
fromPrim, fromOK := fromRep.(types.PrimitiveType)
toPrim, toOK := toRep.(types.PrimitiveType)
if fromSlice, ok := fromRep.(types.SliceType); ok {
if toPtr, ok := toRep.(types.PointerType); ok {
if toPtr.Base.Equals(types.PrimitiveVoid) || fromSlice.Base.Equals(toPtr.Base) {
fmt.Fprintf(out, "%s = extractvalue %s %s, 0", e.ValueIDEmit(c.Dest), e.TypeEmit(from), e.OperandEmit(c.From))
return
}
}
}
// LLVM integer types do not encode signedness, and pointer-sized QK
// integers may have the same representation as a fixed-width integer.
// LLVM rejects casts such as `sext i64 to i64`; retain the SSA destination
// with a representation-preserving select instead.
if e.TypeEmit(from) == e.TypeEmit(to) {
typeName := e.TypeEmit(to)
operand := e.OperandEmit(c.From)
fmt.Fprintf(out, "%s = select i1 true, %s %s, %s %s", e.ValueIDEmit(c.Dest), typeName, operand, typeName, operand)
return
}
if from.Equals(to) {
fmt.Fprintf(out, "%s = bitcast %s %s to %s", e.ValueIDEmit(c.Dest), e.TypeEmit(from), e.OperandEmit(c.From), e.TypeEmit(to))
return
}
if fromOK && toOK {
if types.IsInteger(fromPrim) && types.IsInteger(toPrim) {
srcBits := e.integerBits(fromPrim)
dstBits := e.integerBits(toPrim)
op := "trunc"
if srcBits == dstBits {
op = "bitcast"
} else if srcBits < dstBits {
if types.IsUnsigned(fromPrim) {
op = "zext"
} else {
op = "sext"
}
}
fmt.Fprintf(out, "%s = %s %s %s to %s", e.ValueIDEmit(c.Dest), op, e.TypeEmit(from), e.OperandEmit(c.From), e.TypeEmit(to))
return
}
if types.IsFloat(fromPrim) && types.IsFloat(toPrim) {
srcBits := types.FloatRank(fromPrim)
dstBits := types.FloatRank(toPrim)
op := "fptrunc"
if srcBits == dstBits {
op = "bitcast"
} else if srcBits < dstBits {
op = "fpext"
}
fmt.Fprintf(out, "%s = %s %s %s to %s", e.ValueIDEmit(c.Dest), op, e.TypeEmit(from), e.OperandEmit(c.From), e.TypeEmit(to))
return
}
if types.IsInteger(fromPrim) && types.IsFloat(toPrim) {
op := "sitofp"
if types.IsUnsigned(fromPrim) {
op = "uitofp"
}
fmt.Fprintf(out, "%s = %s %s %s to %s", e.ValueIDEmit(c.Dest), op, e.TypeEmit(from), e.OperandEmit(c.From), e.TypeEmit(to))
return
}
if types.IsFloat(fromPrim) && types.IsInteger(toPrim) {
op := "fptosi"
if types.IsUnsigned(toPrim) {
op = "fptoui"
}
fmt.Fprintf(out, "%s = %s %s %s to %s", e.ValueIDEmit(c.Dest), op, e.TypeEmit(from), e.OperandEmit(c.From), e.TypeEmit(to))
return
}
if fromPrim.Equals(types.PrimitiveBool) && types.IsInteger(toPrim) {
fmt.Fprintf(out, "%s = zext %s %s to %s", e.ValueIDEmit(c.Dest), e.TypeEmit(from), e.OperandEmit(c.From), e.TypeEmit(to))
return
}
if types.IsInteger(fromPrim) && toPrim.Equals(types.PrimitiveBool) {
fmt.Fprintf(out, "%s = trunc %s %s to %s", e.ValueIDEmit(c.Dest), e.TypeEmit(from), e.OperandEmit(c.From), e.TypeEmit(to))
return
}
}
if types.IsPointer(fromRep) && types.IsPointer(toRep) {
fmt.Fprintf(out, "%s = bitcast %s %s to %s", e.ValueIDEmit(c.Dest), e.TypeEmit(from), e.OperandEmit(c.From), e.TypeEmit(to))
return
}
if types.IsPointer(fromRep) && toOK && types.IsInteger(toPrim) {
fmt.Fprintf(out, "%s = ptrtoint %s %s to %s", e.ValueIDEmit(c.Dest), e.TypeEmit(from), e.OperandEmit(c.From), e.TypeEmit(to))
return
}
if fromOK && types.IsInteger(fromPrim) && types.IsPointer(toRep) {
fmt.Fprintf(out, "%s = inttoptr %s %s to %s", e.ValueIDEmit(c.Dest), e.TypeEmit(from), e.OperandEmit(c.From), e.TypeEmit(to))
return
}
panic(fmt.Sprintf("unsupported cast from %v (%T) to %v (%T)", from, from, to, to))
}
func (e *Emitter) SizeofEmit(out *strings.Builder, s ir.Sizeof) {
fmt.Fprintf(
out,
"%s = ptrtoint ptr getelementptr (%s, ptr null, i32 1) to %s",
e.ValueIDEmit(s.Dest),
e.TypeEmit(s.Type),
e.TypeEmit(types.PrimitiveUsz),
)
}
func (e *Emitter) AlignofEmit(out *strings.Builder, a ir.Alignof) {
fmt.Fprintf(
out,
"%s = ptrtoint ptr getelementptr ({ i8, %s }, ptr null, i32 0, i32 1) to %s",
e.ValueIDEmit(a.Dest),
e.TypeEmit(a.Type),
e.TypeEmit(types.PrimitiveUsz),
)
}
func (e *Emitter) OffsetofEmit(out *strings.Builder, o ir.Offsetof) {
if _, ok := types.Underlying(o.Type).(types.UnionType); ok {
fmt.Fprintf(out, "%s = add %s 0, 0", e.ValueIDEmit(o.Dest), e.TypeEmit(types.PrimitiveUsz))
return
}
field := e.structFieldIndex(types.Underlying(o.Type), o.Field)
fmt.Fprintf(
out,
"%s = ptrtoint ptr getelementptr (%s, ptr null, i32 0, i32 %d) to %s",
e.ValueIDEmit(o.Dest),
e.TypeEmit(o.Type),
field,
e.TypeEmit(types.PrimitiveUsz),
)
}