package emit import ( "git.smesh.lol/moxie/pkg/syntax" "git.smesh.lol/moxie/pkg/ssa" "git.smesh.lol/moxie/pkg/token" "git.smesh.lol/moxie/pkg/types" ) func (e *irEmitter) emitBinOp(b *ssa.SSABinOp) { if b.X == nil || b.X.SSAType() == nil { recov := false if b.X == nil { for _, blk := range e.curFunc.Blocks { for i, inst := range blk.Instrs { if inst == b && i > 0 { if prev, ok := blk.Instrs[i-1].(*ssa.SSAUnOp); ok && prev.Op == ssa.OpMul { b.X = prev recov = true } } } if recov { break } } } if !recov && b.X != nil { rt := e.resolvedType(b.X, "i32") if rt != "i32" && rt != "" { reg := e.regName(b) lv := e.operand(b.X) rv := e.operand(b.Y) isCmp := b.Op == ssa.OpEql || b.Op == ssa.OpNeq || b.Op == ssa.OpLss || b.Op == ssa.OpGtr || b.Op == ssa.OpLeq || b.Op == ssa.OpGeq if isCmp && len(rt) > 0 && rt[0] == '{' && rt != "{ptr, ptr}" && rt != e.sliceType() && rt != e.ifaceType() { e.emitStructCompareByLLVM(reg, b.Op, rt, lv, rv) return } op := e.llvmBinOp(b.Op, nil) if rt == "float" || rt == "double" { op = e.floatBinOp(b.Op) lv = ensureTokenFloatLit(lv) rv = ensureTokenFloatLit(rv) } e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(op) ; e.w(" ") ; e.w(rt) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(rv) ; e.w("\n") if !isCmp { e.setRegType(b, reg, rt) } return } } if !recov { e.emitZeroReg(e.regName(b), b.SSAType()) return } } reg := e.regName(b) lt := e.llvmType(b.X.SSAType()) if lt == "void" && b.Y != nil { lt = e.llvmType(b.Y.SSAType()) } if at, ok := e.allocTypes[b.X]; ok && at != "ptr" && at != "void" && at != lt { isScalarToAgg := len(lt) > 0 && (lt[0] == 'i' || lt == "double" || lt == "float") && len(at) > 0 && at[0] == '{' isAggToScalar := len(lt) > 0 && lt[0] == '{' && len(at) > 0 && (at[0] == 'i' || at == "double" || at == "float") if !isScalarToAgg && !isAggToScalar { lt = at } } lv := e.operand(b.X) rv := e.operand(b.Y) if (b.Op == ssa.OpAdd || b.Op == ssa.OpOr) && b.X.SSAType() != nil { if sl, ok := types.SafeUnderlying(b.X.SSAType()).(*types.Slice); ok { e.emitSliceConcat(reg, sl, lv, rv) return } if e.isStringLike(b.X.SSAType()) { e.emitSliceConcat(reg, types.NewSlice(types.Typ[types.Uint8]), lv, rv) return } } if b.X.SSAType() != nil && e.isStringLike(b.X.SSAType()) && (lt == e.sliceType() || lt == "ptr" || lt == "void") { isActuallyIface := false if at, ok := e.allocTypes[b.X]; ok && at == e.ifaceType() { isActuallyIface = true } rvOK := true if b.Y != nil { rvType := e.llvmType(b.Y.SSAType()) rvResolved := e.resolvedType(b.Y, rvType) if load, ok := b.Y.(*ssa.SSAUnOp); ok && load.Op == ssa.OpMul { if g, ok2 := load.X.(*ssa.SSAGlobal); ok2 { if gt, ok3 := e.globalTypes[e.globalName(g)]; ok3 { rvResolved = gt } } } if e.intBits(rvResolved) > 0 { rvOK = false } } if !isActuallyIface && rvOK { e.emitStringCompare(reg, b.Op, lv, rv) return } } if lt == e.sliceType() { rvOK2 := true if b.Y != nil { rvType2 := e.llvmType(b.Y.SSAType()) rvResolved2 := e.resolvedType(b.Y, rvType2) if load, ok := b.Y.(*ssa.SSAUnOp); ok && load.Op == ssa.OpMul { if g, ok2 := load.X.(*ssa.SSAGlobal); ok2 { if gt, ok3 := e.globalTypes[e.globalName(g)]; ok3 { rvResolved2 = gt } } } if e.intBits(rvResolved2) > 0 { rvOK2 = false } } if rvOK2 { e.emitStringCompare(reg, b.Op, lv, rv) return } } if (b.Op == ssa.OpEql || b.Op == ssa.OpNeq) && (rv == "null" || rv == "zeroinitializer" || lv == "null" || lv == "zeroinitializer") && b.X.SSAType() != nil { u := types.SafeUnderlying(b.X.SSAType()) _, isIface := u.(*types.TCInterface) _, isSlice := u.(*types.Slice) _, isSig := u.(*types.Signature) _, isPtr := u.(*types.Pointer) _, isMap := u.(*types.TCMap) _, isChan := u.(*types.TCChan) if isMap || isChan { isPtr = true } if !isIface && !isSlice && !isSig && !isPtr && u == nil && (lt == "{ptr, ptr}" || lt == "{ptr, i64}") { isIface = true } if isSig { e.nextReg++ extReg := "%ne" | irItoa(e.nextReg) aggVal := lv if lv == "null" || lv == "zeroinitializer" { aggVal = rv } e.w(" ") ; e.w(extReg) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(aggVal) ; e.w(", 1\n") cmpOp := "icmp eq" if b.Op == ssa.OpNeq { cmpOp = "icmp ne" } e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(extReg) ; e.w(", null\n") return } if isIface || isSlice || e.isStringLike(b.X.SSAType()) { e.nextReg++ extReg := "%ne" | irItoa(e.nextReg) aggVal := lv if lv == "null" || lv == "zeroinitializer" { aggVal = rv } e.w(" ") ; e.w(extReg) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(aggVal) ; e.w(", 0\n") cmpOp := "icmp eq" if b.Op == ssa.OpNeq { cmpOp = "icmp ne" } e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(extReg) ; e.w(", null\n") return } if isPtr { cmpOp := "icmp eq" if b.Op == ssa.OpNeq { cmpOp = "icmp ne" } ptrVal := lv if lv == "null" || lv == "zeroinitializer" { ptrVal = rv } e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(ptrVal) ; e.w(", null\n") return } } if (b.Op == ssa.OpEql || b.Op == ssa.OpNeq) && len(lt) > 0 && lt[0] == '{' && lt != "{ptr, ptr}" && lt != e.sliceType() && lt != e.ifaceType() { if b.X.SSAType() != nil { if st, ok := types.SafeUnderlying(b.X.SSAType()).(*types.TCStruct); ok { slt := e.llvmStructType(st) e.emitStructCompare(reg, b.Op, st, slt, lv, rv) return } } e.emitStructCompareByLLVM(reg, b.Op, lt, lv, rv) return } if (b.Op == ssa.OpEql || b.Op == ssa.OpNeq) && b.X.SSAType() != nil { if st, okta := types.SafeUnderlying(b.X.SSAType()).(*types.TCStruct); okta && (st != nil && len(lt) > 0 && lt[0] == '{') { slt := e.llvmStructType(st) if slt == lt || (lt != e.ifaceType() && lt != e.sliceType()) { e.emitStructCompare(reg, b.Op, st, slt, lv, rv) return } } if ar, ok := types.SafeUnderlying(b.X.SSAType()).(*types.Array); ok && len(lt) > 0 && lt[0] == '[' { e.emitArrayCompare(reg, b.Op, ar, lt, lv, rv) return } u2 := types.SafeUnderlying(b.X.SSAType()) _, isSig2 := u2.(*types.Signature) _, isIfce2 := u2.(*types.TCInterface) if !isSig2 && !isIfce2 && u2 == nil && lt == "{ptr, ptr}" { isIfce2 = true } if isSig2 || isIfce2 { nilField := "0" if isSig2 { nilField = "1" } rt2 := "ptr" if b.Y != nil && b.Y.SSAType() != nil { rt2 = e.llvmType(b.Y.SSAType()) } if lt == "{ptr, ptr}" && rt2 == "ptr" { e.nextReg++ extReg := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(extReg) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(lv) ; e.w(", ") ; e.w(nilField) ; e.w("\n") cmpOp := "icmp eq" if b.Op == ssa.OpNeq { cmpOp = "icmp ne" } e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(extReg) ; e.w(", ") ; e.w(rv) ; e.w("\n") return } if lt == "ptr" && rt2 == "{ptr, ptr}" { e.nextReg++ extReg := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(extReg) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(rv) ; e.w(", ") ; e.w(nilField) ; e.w("\n") cmpOp := "icmp eq" if b.Op == ssa.OpNeq { cmpOp = "icmp ne" } e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(lv) ; e.w(", ") ; e.w(extReg) ; e.w("\n") return } if lt == "{ptr, ptr}" && rt2 != "{ptr, ptr}" && rt2 != "ptr" { sty := e.sliceType() e.nextReg++ tmp := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(rt2) ; e.w("\n") e.w(" store ") ; e.w(rt2) ; e.w(" ") ; e.w(rv) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n") e.nextReg++ dp := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(dp) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(lv) ; e.w(", 1\n") if rt2 == sty { e.nextReg++ ldp := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(ldp) ; e.w(" = load ") ; e.w(sty) ; e.w(", ptr ") ; e.w(dp) ; e.w("\n") e.nextReg++ lpA := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(lpA) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(ldp) ; e.w(", 0\n") e.nextReg++ lpB := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(lpB) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(", 0\n") e.nextReg++ llA := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(llA) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(ldp) ; e.w(", 1\n") e.nextReg++ llB := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(llB) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(", 1\n") e.nextReg++ cA := "%fc" | irItoa(e.nextReg) e.nextReg++ cB := "%fc" | irItoa(e.nextReg) cmpOp := "icmp eq" combOp := "and" if b.Op == ssa.OpNeq { cmpOp = "icmp ne" combOp = "or" } e.w(" ") ; e.w(cA) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(lpA) ; e.w(", ") ; e.w(lpB) ; e.w("\n") e.w(" ") ; e.w(cB) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(llA) ; e.w(", ") ; e.w(llB) ; e.w("\n") e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(combOp) ; e.w(" i1 ") ; e.w(cA) ; e.w(", ") ; e.w(cB) ; e.w("\n") return } cmpOp := "icmp eq" if b.Op == ssa.OpNeq { cmpOp = "icmp ne" } e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(dp) ; e.w(", ") ; e.w(tmp) ; e.w("\n") return } e.nextReg++ pA := "%fc" | irItoa(e.nextReg) e.nextReg++ pB := "%fc" | irItoa(e.nextReg) e.nextReg++ qA := "%fc" | irItoa(e.nextReg) e.nextReg++ qB := "%fc" | irItoa(e.nextReg) e.nextReg++ cA := "%fc" | irItoa(e.nextReg) e.nextReg++ cB := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(pA) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(lv) ; e.w(", 0\n") e.w(" ") ; e.w(pB) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(rv) ; e.w(", 0\n") e.w(" ") ; e.w(qA) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(lv) ; e.w(", 1\n") e.w(" ") ; e.w(qB) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(rv) ; e.w(", 1\n") cmpOp := "icmp eq" combOp := "and" if b.Op == ssa.OpNeq { cmpOp = "icmp ne" combOp = "or" } e.w(" ") ; e.w(cA) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(pA) ; e.w(", ") ; e.w(pB) ; e.w("\n") e.w(" ") ; e.w(cB) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(qA) ; e.w(", ") ; e.w(qB) ; e.w("\n") e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(combOp) ; e.w(" i1 ") ; e.w(cA) ; e.w(", ") ; e.w(cB) ; e.w("\n") return } } if b.Op == ssa.OpEql || b.Op == ssa.OpNeq { rvt := "" if b.Y != nil && b.Y.SSAType() != nil { rvt = e.llvmType(b.Y.SSAType()) } if lt == "ptr" && rvt == "{ptr, ptr}" { e.nextReg++ extReg := "%pi" | irItoa(e.nextReg) e.w(" ") ; e.w(extReg) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(rv) ; e.w(", 1\n") cmpOp := "icmp eq" if b.Op == ssa.OpNeq { cmpOp = "icmp ne" } e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(lv) ; e.w(", ") ; e.w(extReg) ; e.w("\n") return } if lt == "{ptr, ptr}" && rvt == "ptr" { e.nextReg++ extReg := "%pi" | irItoa(e.nextReg) e.w(" ") ; e.w(extReg) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(lv) ; e.w(", 1\n") cmpOp := "icmp eq" if b.Op == ssa.OpNeq { cmpOp = "icmp ne" } e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(extReg) ; e.w(", ") ; e.w(rv) ; e.w("\n") return } } if b.Op == ssa.OpAndNot { rt := "" if b.Y != nil && b.Y.SSAType() != nil { rt = e.llvmType(b.Y.SSAType()) rt = e.resolvedType(b.Y, rt) } lt = e.resolvedType(b.X, lt) if rt != "" && rt != lt && e.intBits(lt) > 0 && e.intBits(rt) > 0 { rv = e.coerceInt(rv, rt, lt) } e.nextReg++ notReg := "%an" | irItoa(e.nextReg) allOnes := "-1" e.w(" ") ; e.w(notReg) ; e.w(" = xor ") ; e.w(lt) ; e.w(" ") ; e.w(rv) ; e.w(", ") ; e.w(allOnes) ; e.w("\n") e.w(" ") ; e.w(reg) ; e.w(" = and ") ; e.w(lt) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(notReg) ; e.w("\n") e.setRegType(b, reg, lt) return } if b.Y == nil || b.Y.SSAType() == nil { e.emitZeroReg(e.regName(b), b.SSAType()) return } rt := e.llvmType(b.Y.SSAType()) atl0 := e.resolvedType(b.X, lt) if atl0 != lt { lt = atl0 } atr0 := e.resolvedType(b.Y, rt) if atr0 != rt { rt = atr0 } coerced := false if lt != rt && e.intBits(lt) > 0 && e.intBits(rt) > 0 { isCmp := b.Op == ssa.OpEql || b.Op == ssa.OpNeq || b.Op == ssa.OpLss || b.Op == ssa.OpGtr || b.Op == ssa.OpLeq || b.Op == ssa.OpGeq resType := e.llvmType(b.SSAType()) if !isCmp && e.intBits(resType) > 0 { if lt != resType { lv = e.coerceInt(lv, lt, resType) lt = resType } if rt != resType { rv = e.coerceInt(rv, rt, resType) } } else if e.intBits(lt) > e.intBits(rt) { rv = e.coerceInt(rv, rt, lt) } else { lv = e.coerceInt(lv, lt, rt) lt = rt } coerced = true } if lt == rt && !coerced { atl := e.resolvedType(b.X, lt) if atl != lt { lt = atl } if b.Y != nil { atr := e.resolvedType(b.Y, rt) if atr != rt { rt = atr } } if lt != rt && e.intBits(lt) > 0 && e.intBits(rt) > 0 { if e.intBits(lt) > e.intBits(rt) { rv = e.coerceInt(rv, rt, lt) rt = lt } else if e.intBits(rt) > e.intBits(lt) { lv = e.coerceInt(lv, lt, rt) lt = rt } } } resultIsInt := e.intBits(e.llvmType(b.SSAType())) > 0 isLF := lt == "double" || lt == "float" isRF := rt == "double" || rt == "float" if !isRF && isConstOperand(rv) && looksLikeFloat(rv) { if resultIsInt { if iv, ok := floatLitToInt(rv); ok { rv = irItoa64(iv) } } else { isRF = true rt = "double" } } if !isLF && isConstOperand(lv) && looksLikeFloat(lv) { if resultIsInt { if iv, ok := floatLitToInt(lv); ok { lv = irItoa64(iv) } } else { isLF = true lt = "double" } } if isLF && isRF { ssaLT := e.llvmType(b.X.SSAType()) ssaRT := "" if b.Y != nil && b.Y.SSAType() != nil { ssaRT = e.llvmType(b.Y.SSAType()) } if !isConstOperand(lv) && e.intBits(ssaLT) > 0 { _, hasRT := e.regTypes[lv] if !hasRT { lv = e.intToFloat(lv, ssaLT, lt) } } if !isConstOperand(rv) && e.intBits(ssaRT) > 0 { _, hasRT := e.regTypes[rv] if !hasRT { rv = e.intToFloat(rv, ssaRT, rt) } } } else if !isLF && isRF && e.intBits(lt) > 0 { if resultIsInt { demoted := false if yc, ok := b.Y.(*ssa.SSAConst); ok { if cf, ok2 := yc.Value().(*types.ConstFloat); ok2 { lit := cf.Lit if lit == "" { lit = cf.String() } if iv, ok3 := floatLitToInt(lit); ok3 { rv = irItoa64(iv) rt = lt isRF = false demoted = true } } } if !demoted { if isConstOperand(lv) { lv = ensureTokenFloatLit(lv) } else { lv = e.intToFloat(lv, lt, rt) } lt = rt } } else { if isConstOperand(lv) { lv = ensureTokenFloatLit(lv) } else { lv = e.intToFloat(lv, lt, rt) } lt = rt } } else if isLF && !isRF && e.intBits(rt) > 0 { if resultIsInt { if xc, ok := b.X.(*ssa.SSAConst); ok { if cf, ok2 := xc.Value().(*types.ConstFloat); ok2 { lit := cf.Lit if lit == "" { lit = cf.String() } if iv, ok3 := floatLitToInt(lit); ok3 { lv = irItoa64(iv) lt = rt isLF = false } } } if isLF { if isConstOperand(rv) { rv = ensureTokenFloatLit(rv) } else { rv = e.intToFloat(rv, rt, lt) } rt = lt } } else { if isConstOperand(rv) { rv = ensureTokenFloatLit(rv) } else { rv = e.intToFloat(rv, rt, lt) } rt = lt } } if lt == "float" && rt == "double" { e.nextReg++ tmp := "%fext" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = fpext float ") ; e.w(lv) ; e.w(" to double\n") lv = tmp lt = "double" } else if lt == "double" && rt == "float" { e.nextReg++ tmp := "%fext" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = fpext float ") ; e.w(rv) ; e.w(" to double\n") rv = tmp rt = "double" } op := e.llvmBinOp(b.Op, b.X.SSAType()) if op == "" { e.w(" ; unsupported binop\n") return } isCmpOp := b.Op == ssa.OpEql || b.Op == ssa.OpNeq || b.Op == ssa.OpLss || b.Op == ssa.OpGtr || b.Op == ssa.OpLeq || b.Op == ssa.OpGeq if lt == "double" || lt == "float" { rv = ensureTokenFloatLit(rv) lv = ensureTokenFloatLit(lv) op = e.floatBinOp(b.Op) if !isCmpOp { e.setRegType(b, reg, lt) } } else if !isCmpOp { ssaLT := e.llvmType(b.X.SSAType()) if ssaLT != lt { e.setRegType(b, reg, lt) } } if len(lt) > 0 && lt[0] == '[' && (b.Op == ssa.OpEql || b.Op == ssa.OpNeq) { e.emitArrayCompareByLLVM(reg, b.Op, lt, lv, rv) return } if lt == "ptr" && !isCmpOp { e.nextReg++ pi := "%pi" | irItoa(e.nextReg) e.w(" ") ; e.w(pi) ; e.w(" = ptrtoint ptr ") ; e.w(lv) ; e.w(" to ") ; e.w(e.intptrType()) ; e.w("\n") e.nextReg++ ri := "%pi" | irItoa(e.nextReg) rvCoerced := rv if rv != "0" && rv != "1" && rv != "-1" { e.w(" ") ; e.w(ri) ; e.w(" = ptrtoint ptr ") ; e.w(rv) ; e.w(" to ") ; e.w(e.intptrType()) ; e.w("\n") rvCoerced = ri } e.nextReg++ ai := "%pi" | irItoa(e.nextReg) e.w(" ") ; e.w(ai) ; e.w(" = ") ; e.w(op) ; e.w(" ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(pi) ; e.w(", ") ; e.w(rvCoerced) ; e.w("\n") e.w(" ") ; e.w(reg) ; e.w(" = inttoptr ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(ai) ; e.w(" to ptr\n") return } if isCmpOp && lt == "i32" && b.Y != nil && b.Y.SSAType() != nil { rvt := e.llvmType(b.Y.SSAType()) rvr := e.resolvedType(b.Y, rvt) if rvr == e.sliceType() || rvt == e.sliceType() { lt = e.sliceType() } } if isCmpOp && lt == "i32" { lr := e.resolvedType(b.X, lt) if lr == e.sliceType() { lt = e.sliceType() } } if lt == e.sliceType() && isCmpOp { rvIsInt := false if b.Y != nil { rvt3 := e.llvmType(b.Y.SSAType()) rvr3 := e.resolvedType(b.Y, rvt3) if e.intBits(rvr3) > 0 { rvIsInt = true } } lvIsInt := false if b.X != nil && b.X.SSAType() != nil { lxt := e.llvmType(b.X.SSAType()) if e.intBits(lxt) > 0 { lvIsInt = true } } if !rvIsInt && !lvIsInt { e.emitStringCompare(reg, b.Op, lv, rv) return } } if isCmpOp && len(lt) > 0 && lt[0] == '{' { e.emitStructCompareByLLVM(reg, b.Op, lt, lv, rv) return } e.w(" ") e.w(reg) e.w(" = ") e.w(op) e.w(" ") e.w(lt) e.w(" ") e.w(lv) e.w(", ") e.w(rv) e.w("\n") } func looksLikeFloat(s string) bool { if len(s) == 0 { return false } if s[0] != '-' && s[0] != '+' && (s[0] < '0' || s[0] > '9') { return false } for i := 0; i < len(s); i++ { if s[i] == '.' || s[i] == 'e' || s[i] == 'E' { return true } } return false } func floatLitToInt(s string) (int64, bool) { if len(s) == 0 { return 0, false } i := 0 neg := false if s[0] == '-' { neg = true i = 1 } else if s[0] == '+' { i = 1 } var intPart int64 for ; i < len(s); i++ { ch := s[i] if ch == '_' { continue } if ch < '0' || ch > '9' { break } intPart = intPart*10 + int64(ch-'0') } var fracDigits int32 if i < len(s) && s[i] == '.' { i++ for ; i < len(s); i++ { ch := s[i] if ch == '_' { continue } if ch < '0' || ch > '9' { break } if ch != '0' { return 0, false } fracDigits++ } } _ = fracDigits exp := 0 if i < len(s) && (s[i] == 'e' || s[i] == 'E') { i++ expNeg := false if i < len(s) && s[i] == '-' { expNeg = true i++ } else if i < len(s) && s[i] == '+' { i++ } for ; i < len(s); i++ { ch := s[i] if ch < '0' || ch > '9' { break } exp = exp*10 + int32(ch-'0') } if expNeg { return 0, false } } result := intPart for j := 0; j < exp; j++ { result = result * 10 if result < 0 { return 0, false } } if neg { result = -result } if result == 0 { return 0, false } return result, true } func isLLVMIntType(s string) bool { if len(s) < 2 || s[0] != 'i' { return false } for j := 1; j < len(s); j++ { if s[j] < '0' || s[j] > '9' { return false } } return true } func isConstOperand(s string) bool { if len(s) == 0 { return false } return s[0] != '%' && s[0] != '@' } func ensureTokenFloatLit(s string) string { if len(s) == 0 || s[0] == '%' || s[0] == '@' { return s } if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') { return s } hasDecimal := false for i := 0; i < len(s); i++ { if s[i] == '.' || s[i] == 'e' || s[i] == 'E' { hasDecimal = true break } } if !hasDecimal { return s | ".0" } return s } func (e *irEmitter) emitSliceConcat(reg string, sl *types.Slice, lv, rv string) { ipt := e.intptrType() sty := "{ptr, " | ipt | ", " | ipt | "}" if isBareLiteral(lv) { lv = "zeroinitializer" } if isBareLiteral(rv) { rv = "zeroinitializer" } elemType := e.llvmType(sl.Elem()) xPtr := e.nextReg2("cc") e.w(" ") ; e.w(xPtr) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(", 0\n") xLen := e.nextReg2("cc") e.w(" ") ; e.w(xLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(", 1\n") yPtr := e.nextReg2("cc") e.w(" ") ; e.w(yPtr) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(", 0\n") yLen := e.nextReg2("cc") e.w(" ") ; e.w(yLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(", 1\n") elemSz := e.nextReg2("cc") e.w(" ") ; e.w(elemSz) e.w(" = ptrtoint ptr getelementptr (") ; e.w(elemType) e.w(", ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n") retTy := "{ptr, " | ipt | ", " | ipt | "}" result := e.nextReg2("cc") e.w(" ") ; e.w(result) e.w(" = call ") ; e.w(retTy) ; e.w(" @runtime.sliceAppend(ptr ") e.w(xPtr) ; e.w(", ptr ") ; e.w(yPtr) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(xLen) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(xLen) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(yLen) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(elemSz) e.w(")\n") newPtr := e.nextReg2("cc") e.w(" ") ; e.w(newPtr) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 0\n") e.scopeTrackAlloc(newPtr) newLen := e.nextReg2("cc") e.w(" ") ; e.w(newLen) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 1\n") newCap := e.nextReg2("cc") e.w(" ") ; e.w(newCap) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 2\n") s1 := e.nextReg2("cc") e.w(" ") ; e.w(s1) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" undef, ptr ") ; e.w(newPtr) ; e.w(", 0\n") s2 := e.nextReg2("cc") e.w(" ") ; e.w(s2) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s1) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(newLen) ; e.w(", 1\n") e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s2) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(newCap) ; e.w(", 2\n") e.declareRuntime("runtime.sliceAppend", retTy, "ptr, ptr, " | ipt | ", " | ipt | ", " | ipt | ", " | ipt) } func isBareLiteral(s string) bool { if len(s) == 0 { return false } return s[0] >= '0' && s[0] <= '9' } func (e *irEmitter) emitStringCompare(reg string, op ssa.SSAOp, lv, rv string) { ipt := e.intptrType() sty := "{ptr, " | ipt | ", " | ipt | "}" if lv == "null" { lv = "zeroinitializer" } if rv == "null" { rv = "zeroinitializer" } if isBareLiteral(lv) { lv = "zeroinitializer" } if isBareLiteral(rv) { rv = "zeroinitializer" } switch op { case ssa.OpEql: e.w(" ") ; e.w(reg) ; e.w(" = call i1 @runtime.stringEqual(") e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(")\n") e.declareRuntime("runtime.stringEqual", "i1", sty | ", " | sty) case ssa.OpNeq: tmp := e.nextReg2("sc") e.w(" ") ; e.w(tmp) ; e.w(" = call i1 @runtime.stringEqual(") e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(")\n") e.w(" ") ; e.w(reg) ; e.w(" = xor i1 ") ; e.w(tmp) ; e.w(", -1\n") e.declareRuntime("runtime.stringEqual", "i1", sty | ", " | sty) case ssa.OpLss: e.w(" ") ; e.w(reg) ; e.w(" = call i1 @runtime.stringLess(") e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(")\n") e.declareRuntime("runtime.stringLess", "i1", sty | ", " | sty) case ssa.OpGtr: e.w(" ") ; e.w(reg) ; e.w(" = call i1 @runtime.stringLess(") e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(", ") ; e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(")\n") e.declareRuntime("runtime.stringLess", "i1", sty | ", " | sty) case ssa.OpLeq: tmp := e.nextReg2("sc") e.w(" ") ; e.w(tmp) ; e.w(" = call i1 @runtime.stringLess(") e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(", ") ; e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(")\n") e.w(" ") ; e.w(reg) ; e.w(" = xor i1 ") ; e.w(tmp) ; e.w(", -1\n") e.declareRuntime("runtime.stringLess", "i1", sty | ", " | sty) case ssa.OpGeq: tmp := e.nextReg2("sc") e.w(" ") ; e.w(tmp) ; e.w(" = call i1 @runtime.stringLess(") e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(")\n") e.w(" ") ; e.w(reg) ; e.w(" = xor i1 ") ; e.w(tmp) ; e.w(", -1\n") e.declareRuntime("runtime.stringLess", "i1", sty | ", " | sty) default: e.w(" ; unsupported string binop\n") } } func (e *irEmitter) emitStructCompare(reg string, op ssa.SSAOp, st *types.TCStruct, lt, lv, rv string) { if st == nil { e.w(" ") ; e.w(reg) ; e.w(" = icmp eq i32 0, 0\n") return } n := st.NumFields() if n == 0 { if op == ssa.OpEql { e.valName[nil] = "true" e.w(" ") ; e.w(reg) ; e.w(" = icmp eq i32 0, 0\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = icmp ne i32 0, 0\n") } return } var lastCmp string for i := 0; i < n; i++ { ft := e.llvmType(st.Field(i).Type()) lf := e.nextReg2("sf") rf := e.nextReg2("sf") e.w(" ") ; e.w(lf) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n") e.w(" ") ; e.w(rf) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(rv) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n") cmp := e.nextReg2("sf") if e.isStringLike(st.Field(i).Type()) { sty := e.sliceType() e.w(" ") ; e.w(cmp) ; e.w(" = call i1 @runtime.stringEqual(") ; e.w(sty) ; e.w(" ") ; e.w(lf) ; e.w(", ") ; e.w(sty) ; e.w(" ") ; e.w(rf) ; e.w(")\n") e.declareRuntime("runtime.stringEqual", "i1", sty | ", " | sty) } else if len(ft) > 0 && ft[0] == '{' { if innerSt, ok2 := types.SafeUnderlying(st.Field(i).Type()).(*types.TCStruct); ok2 { e.emitStructCompare(cmp, ssa.OpEql, innerSt, ft, lf, rf) } else { e.w(" ") ; e.w(cmp) ; e.w(" = icmp eq i32 0, 0 ; nested struct fallback\n") } } else { if ft == "void" { ft = "ptr" } cmpOp := "icmp eq" if ft == "float" || ft == "double" { cmpOp = "fcmp oeq" } e.w(" ") ; e.w(cmp) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ") ; e.w(ft) ; e.w(" ") ; e.w(lf) ; e.w(", ") ; e.w(rf) ; e.w("\n") } if i == 0 { lastCmp = cmp } else { acc := e.nextReg2("sf") e.w(" ") ; e.w(acc) ; e.w(" = and i1 ") ; e.w(lastCmp) ; e.w(", ") ; e.w(cmp) ; e.w("\n") lastCmp = acc } } if op == ssa.OpNeq { e.w(" ") ; e.w(reg) ; e.w(" = xor i1 ") ; e.w(lastCmp) ; e.w(", -1\n") } else if n == 1 { e.w(" ") ; e.w(reg) ; e.w(" = and i1 ") ; e.w(lastCmp) ; e.w(", true\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = and i1 ") ; e.w(lastCmp) ; e.w(", true\n") } } func (e *irEmitter) emitArrayCompare(reg string, op ssa.SSAOp, ar *types.Array, lt, lv, rv string) { n := int32(ar.Len()) if n == 0 { if op == ssa.OpEql { e.w(" ") ; e.w(reg) ; e.w(" = icmp eq i32 0, 0\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = icmp ne i32 0, 0\n") } return } et := e.llvmType(ar.Elem()) isZeroL := lv == "0" || lv == "zeroinitializer" isZeroR := rv == "0" || rv == "zeroinitializer" var lastCmp string for i := 0; i < n; i++ { var lfr, rfr string if isZeroL { lfr = "0" } else { lfr = e.nextReg2("ae") e.w(" ") ; e.w(lfr) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n") } if isZeroR { rfr = "0" } else { rfr = e.nextReg2("ae") e.w(" ") ; e.w(rfr) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(rv) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n") } cmp := e.nextReg2("ae") aeCmpOp := "icmp eq" if et == "float" || et == "double" { aeCmpOp = "fcmp oeq" } e.w(" ") ; e.w(cmp) ; e.w(" = ") ; e.w(aeCmpOp) ; e.w(" ") ; e.w(et) ; e.w(" ") ; e.w(lfr) ; e.w(", ") ; e.w(rfr) ; e.w("\n") if i == 0 { lastCmp = cmp } else { acc := e.nextReg2("ae") e.w(" ") ; e.w(acc) ; e.w(" = and i1 ") ; e.w(lastCmp) ; e.w(", ") ; e.w(cmp) ; e.w("\n") lastCmp = acc } } if op == ssa.OpNeq { e.w(" ") ; e.w(reg) ; e.w(" = xor i1 ") ; e.w(lastCmp) ; e.w(", -1\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = and i1 ") ; e.w(lastCmp) ; e.w(", true\n") } } func parseArrayType(lt string) (int32, string) { if len(lt) < 5 || lt[0] != '[' { return 0, "" } i := 1 for i < len(lt) && lt[i] >= '0' && lt[i] <= '9' { i++ } n := 0 for j := 1; j < i; j++ { n = n*10 + int32(lt[j]-'0') } if i+3 >= len(lt) || lt[i] != ' ' || lt[i+1] != 'x' || lt[i+2] != ' ' { return 0, "" } et := lt[i+3 : len(lt)-1] return n, et } func (e *irEmitter) emitArrayCompareByLLVM(reg string, op ssa.SSAOp, lt, lv, rv string) { n, et := parseArrayType(lt) if n == 0 { e.w(" ") ; e.w(reg) ; e.w(" = icmp eq i32 0, 0\n") return } isZeroL := lv == "0" || lv == "zeroinitializer" isZeroR := rv == "0" || rv == "zeroinitializer" var lastCmp string for i := 0; i < n; i++ { var lfr, rfr string if isZeroL { lfr = "0" } else { lfr = e.nextReg2("ae") e.w(" ") ; e.w(lfr) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n") } if isZeroR { rfr = "0" } else { rfr = e.nextReg2("ae") e.w(" ") ; e.w(rfr) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(rv) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n") } cmp := e.nextReg2("ae") aeCmpOp := "icmp eq" if et == "float" || et == "double" { aeCmpOp = "fcmp oeq" } e.w(" ") ; e.w(cmp) ; e.w(" = ") ; e.w(aeCmpOp) ; e.w(" ") ; e.w(et) ; e.w(" ") ; e.w(lfr) ; e.w(", ") ; e.w(rfr) ; e.w("\n") if i == 0 { lastCmp = cmp } else { acc := e.nextReg2("ae") e.w(" ") ; e.w(acc) ; e.w(" = and i1 ") ; e.w(lastCmp) ; e.w(", ") ; e.w(cmp) ; e.w("\n") lastCmp = acc } } if op == ssa.OpNeq { e.w(" ") ; e.w(reg) ; e.w(" = xor i1 ") ; e.w(lastCmp) ; e.w(", -1\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = and i1 ") ; e.w(lastCmp) ; e.w(", true\n") } } func (e *irEmitter) emitStructCompareByLLVM(reg string, op ssa.SSAOp, lt, lv, rv string) { fields := parseStructFields(lt) if len(fields) == 0 { e.w(" ") ; e.w(reg) ; e.w(" = icmp eq i32 0, 0\n") return } var lastCmp string for i, ft := range fields { lf := e.nextReg2("sf") rf := e.nextReg2("sf") e.w(" ") ; e.w(lf) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n") e.w(" ") ; e.w(rf) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(rv) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n") cmp := e.nextReg2("sf") if len(ft) > 0 && ft[0] == '{' { e.emitStructCompareByLLVM(cmp, ssa.OpEql, ft, lf, rf) } else if len(ft) > 0 && ft[0] == '[' { e.emitArrayCompareByLLVM(cmp, ssa.OpEql, ft, lf, rf) } else { cmpOp := "icmp eq" if ft == "float" || ft == "double" { cmpOp = "fcmp oeq" } e.w(" ") ; e.w(cmp) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ") ; e.w(ft) ; e.w(" ") ; e.w(lf) ; e.w(", ") ; e.w(rf) ; e.w("\n") } if i == 0 { lastCmp = cmp } else { acc := e.nextReg2("sf") e.w(" ") ; e.w(acc) ; e.w(" = and i1 ") ; e.w(lastCmp) ; e.w(", ") ; e.w(cmp) ; e.w("\n") lastCmp = acc } } if op == ssa.OpNeq { e.w(" ") ; e.w(reg) ; e.w(" = xor i1 ") ; e.w(lastCmp) ; e.w(", -1\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = and i1 ") ; e.w(lastCmp) ; e.w(", true\n") } } func (e *irEmitter) llvmBinOp(op ssa.SSAOp, typ syntax.Type) string { isFloat := false isSigned := true if typ != nil { if b, ok := types.SafeUnderlying(typ).(*types.Basic); ok { if b.Info()&types.IsFloat != 0 { isFloat = true } if b.Info()&types.IsUnsigned != 0 { isSigned = false } } } if isFloat { switch op { case ssa.OpAdd: return "fadd" case ssa.OpSub: return "fsub" case ssa.OpMul: return "fmul" case ssa.OpQuo: return "fdiv" case ssa.OpEql: return "fcmp oeq" case ssa.OpNeq: return "fcmp une" case ssa.OpLss: return "fcmp olt" case ssa.OpLeq: return "fcmp ole" case ssa.OpGtr: return "fcmp ogt" case ssa.OpGeq: return "fcmp oge" } return "" } switch op { case ssa.OpAdd: return "add" case ssa.OpSub: return "sub" case ssa.OpMul: return "mul" case ssa.OpQuo: if isSigned { return "sdiv" } return "udiv" case ssa.OpRem: if isSigned { return "srem" } return "urem" case ssa.OpAnd, ssa.OpLand: return "and" case ssa.OpOr, ssa.OpLor: return "or" case ssa.OpXor: return "xor" case ssa.OpShl: return "shl" case ssa.OpShr: if isSigned { return "ashr" } return "lshr" case ssa.OpAndNot: return "" case ssa.OpEql: return "icmp eq" case ssa.OpNeq: return "icmp ne" case ssa.OpLss: if isSigned { return "icmp slt" } return "icmp ult" case ssa.OpLeq: if isSigned { return "icmp sle" } return "icmp ule" case ssa.OpGtr: if isSigned { return "icmp sgt" } return "icmp ugt" case ssa.OpGeq: if isSigned { return "icmp sge" } return "icmp uge" } return "" } func (e *irEmitter) instrRefsValue(instr ssa.SSAInstruction, v ssa.SSAValue) bool { switch i := instr.(type) { case *ssa.SSASlice: return i.X == v || i.Low == v || i.High == v || i.Max == v case *ssa.SSAStore: return i.Val == v || i.Addr == v case *ssa.SSACall: if i.Call.Value == v { return true } for _, arg := range i.Call.Args { if arg == v { return true } } case *ssa.SSAReturn: for _, res := range i.Results { if res == v { return true } } case *ssa.SSAPhi: for _, ed := range i.Edges { if ed == v { return true } } case *ssa.SSABinOp: return i.X == v || i.Y == v case *ssa.SSAUnOp: return i.X == v case *ssa.SSAFieldAddr: return i.X == v case *ssa.SSAIndexAddr: return i.X == v || i.Index == v case *ssa.SSAConvert: return i.X == v case *ssa.SSAChangeType: return i.X == v case *ssa.SSAMakeInterface: return i.X == v case *ssa.SSATypeAssert: return i.X == v case *ssa.SSAIf: return i.Cond == v case *ssa.SSALookup: return i.X == v || i.Index == v } return false } func (e *irEmitter) arrayDerefOnlyUsedBySlice(u *ssa.SSAUnOp) bool { fn := u.InstrParent() if fn == nil { return false } hasSliceUse := false for _, b := range fn.Blocks { for _, instr := range b.Instrs { if instr == u { continue } if sl, ok := instr.(*ssa.SSASlice); ok && sl.X == u { hasSliceUse = true continue } if e.instrRefsValue(instr, u) { return false } } } return hasSliceUse } func (e *irEmitter) emitUnOp(u *ssa.SSAUnOp) { reg := e.regName(u) if u.Op == ssa.OpMul { loadType := e.llvmType(u.SSAType()) if loadType == "void" { if at, ok := e.allocTypes[u.X]; ok { loadType = at } else if a, ok := u.X.(*ssa.SSAAlloc); ok { loadType = e.inferAllocTypeFromStores(a) } else if ia, ok := u.X.(*ssa.SSAIndexAddr); ok { et := e.llvmType(ia.SSAType()) if p2, ok2 := types.SafeUnderlying(ia.SSAType()).(*types.Pointer); ok2 && p2.Elem() != nil { et = e.llvmType(p2.Elem()) } if et == "void" { et = "i8" } loadType = et } else { loadType = "ptr" } e.allocTypes[u] = loadType } if g, ok := u.X.(*ssa.SSAGlobal); ok { loadType = e.resolveGlobalDeclType(g) } if at, ok := e.allocTypes[u.X]; ok && at != "ptr" && at != "void" && at != loadType { bothScalar := len(loadType) > 0 && loadType[0] == 'i' && len(at) > 0 && at[0] == 'i' isArrayElem := len(at) > 0 && at[0] == '[' && len(loadType) > 0 && loadType[0] != '{' && loadType != at bothAgg := len(loadType) > 0 && loadType[0] == '{' && len(at) > 0 && at[0] == '{' ssaIsScalar := len(loadType) > 0 && (loadType[0] == 'i' || loadType == "float" || loadType == "double") && loadType != "void" allocIsAgg := len(at) > 0 && at[0] == '{' ssaIsAgg := len(loadType) > 0 && loadType[0] == '{' allocIsScalar := len(at) > 0 && (at[0] == 'i' || at == "double" || at == "float") if !bothScalar && !isArrayElem && !bothAgg && !(ssaIsScalar && allocIsAgg) && !(ssaIsAgg && allocIsScalar) { loadType = at e.allocTypes[u] = loadType } } if _, isIA := u.X.(*ssa.SSAIndexAddr); isIA && len(loadType) > 0 && loadType[0] == '[' { if at, ok2 := e.allocTypes[u.X]; ok2 && at != loadType { loadType = at e.allocTypes[u] = loadType } } if len(loadType) > 0 && loadType[0] == '[' && e.arrayDerefOnlyUsedBySlice(u) { addr := e.operand(u.X) e.valName[u] = addr e.allocTypes[u] = loadType return } addr := e.operand(u.X) e.w(" ") e.w(reg) e.w(" = load ") e.w(loadType) e.w(", ptr ") e.w(addr) e.w("\n") if loadType == "double" || loadType == "float" { e.setRegType(u, reg, loadType) } return } valType := e.llvmType(u.X.SSAType()) resolved := e.resolvedType(u.X, valType) if resolved != valType { valType = resolved } val := e.operand(u.X) if u.Op == ssa.OpSub { isFloat := false if b, ok := types.SafeUnderlying(u.X.SSAType()).(*types.Basic); ok { isFloat = b.Info()&types.IsFloat != 0 } if !isFloat && (valType == "double" || valType == "float") { isFloat = true } if !isFloat && isConstOperand(val) && looksLikeFloat(val) { isFloat = true if valType != "float" && valType != "double" { valType = "double" } } e.w(" ") e.w(reg) if isFloat { e.w(" = fneg ") e.w(valType) e.w(" ") e.w(val) e.w("\n") e.setRegType(u, reg, valType) } else { e.w(" = sub ") e.w(valType) e.w(" 0, ") e.w(val) e.w("\n") } return } if u.Op == ssa.OpNot { e.w(" ") e.w(reg) e.w(" = xor i1 ") e.w(val) e.w(", true\n") e.setRegType(u, reg, "i1") return } if u.Op == ssa.OpXor { e.w(" ") e.w(reg) e.w(" = xor ") e.w(valType) e.w(" ") e.w(val) e.w(", -1\n") return } if u.Op == ssa.OpArrow { e.emitChanRecv(u) return } e.w(" ; unsupported unop op=") e.w(u.Op.String()) e.w("\n") }