package codegen import ( "fmt" "strconv" "strings" "github.com/marzeq/quokka/parser" "github.com/marzeq/quokka/shared" "github.com/marzeq/quokka/typechecker" ) type ( Node = parser.Node Type = shared.Type ) type CodeGen struct { tmpVarId uint tmpLblId uint cnstId uint rootNode *Node funcSigs map[string]*typechecker.FunctionSig typeTable shared.TypeTable } func NewCodeGen(rootNode *Node, funcSigs map[string]*typechecker.FunctionSig, typeTable shared.TypeTable) *CodeGen { return &CodeGen{ tmpVarId: 0, tmpLblId: 0, cnstId: 0, rootNode: rootNode, funcSigs: funcSigs, typeTable: typeTable, } } func (cg *CodeGen) GetTmpVar() string { val := fmt.Sprintf("%%___tmpvar%d", cg.tmpVarId) cg.tmpVarId++ return val } func (cg *CodeGen) GetTmpLbl() string { val := fmt.Sprintf("@___tmplbl%d", cg.tmpLblId) cg.tmpLblId++ return val } func (cg *CodeGen) GetCnst() string { val := fmt.Sprintf("$___cnst%d", cg.cnstId) cg.cnstId++ return val } func (cg *CodeGen) EmitIR() (string, error) { ir := "" gsetups := []string{} for _, node := range cg.rootNode.Children { if node.Type == parser.NODE_TYPE_STRUCT_DEF { continue } if node.Type != parser.NODE_TYPE_FUNCTION_DEF { return "", shared.NewError(node.Loc, "unexpected node type %s", node.Type) } if len(node.Children) == 0 { continue } funcIr, gstps, err := cg.GenerateFuncIR(node) if err != nil { return "", err } ir += funcIr gsetups = append(gsetups, gstps...) } for name, tpe := range cg.typeTable { if !tpe.IsStruct() { continue } ir += fmt.Sprintf("type :%s = {", name) for _, field := range tpe.(shared.Struct).Fields { ir += fmt.Sprintf(" %s,", mapTypeToIRType(field.R)) } ir += "}\n" } for _, gsetup := range gsetups { ir = gsetup + "\n" + ir } return ir, nil } func (cg *CodeGen) GenerateFuncIR(funcNode *Node) (string, []string, error) { cg.tmpVarId = 0 cg.tmpLblId = 0 prologue := "" body := "" epilogue := "" gsetups := []string{} fname := typechecker.IdentToStr(funcNode.Value.(*parser.FunctionValue).Name) fsig := cg.funcSigs[fname] if fsig.Name == "main" { prologue += "export function w $main() {\n@start" } else { prologue += fmt.Sprintf("function %s $%s(", mapTypeToIRType(fsig.RetType), fsig.Name) after := "" for i, arg := range fsig.ArgTypes { tnm := cg.GetTmpVar() tpe := mapTypeToIRType(arg.R) tsz := shared.GetSizeOfType(arg.R) prologue += fmt.Sprintf("%s %s", tpe, tnm) if i != len(fsig.ArgTypes)-1 { prologue += ", " } after += fmt.Sprintf("%%%s =l alloc%d %d\n", arg.L, tsz, tsz) after += fmt.Sprintf("store%s %s, %%%s", tpe, tnm, arg.L) } prologue += ") {\n@start\n" prologue += after if fsig.ImplicitReturn { epilogue = "\nret" } } epilogue += "\n}\n" child := funcNode.Children[0] if child.Type == parser.NODE_TYPE_BLOCK { b, gstps, _, err := cg.GenerateBlockIR(child, "", "") if err != nil { return "", nil, err } body = b if len(child.Children) > 1 && child.Children[len(child.Children)-1].Type == parser.NODE_TYPE_IF && fsig.RetType != shared.PRIMITIVE_VOID { epilogue = "ret 0" + epilogue } gsetups = append(gsetups, gstps...) } else { val, setups, gstps, _, err := cg.GenerateExprIR(child) if err != nil { return "", nil, err } for _, setup := range setups { body += "\n" + setup } body += "\nret " + val gsetups = append(gsetups, gstps...) } return prologue + body + epilogue, gsetups, nil } func (cg *CodeGen) GenerateBlockIR(blockNode *Node, loopBegin, loopEnd string) (string, []string, bool, error) { body := "" endsWithTerminator := false gsetups := []string{} for i, node := range blockNode.Children { ir, setups, gstps, err := cg.GenerateStmtIR(node, i == len(blockNode.Children)-1, loopBegin, loopEnd) if err != nil { return "", nil, false, err } for _, setup := range setups { body += "\n" + setup } body += "\n" + ir if IsTerminatorInstruction(ir) { endsWithTerminator = true } else { endsWithTerminator = false } gsetups = append(gsetups, gstps...) } return body, gsetups, endsWithTerminator, nil } func IsTerminatorInstruction(ir string) bool { return strings.HasPrefix(ir, "ret") || strings.HasPrefix(ir, "jmp") || strings.HasPrefix(ir, "jnz") } func (cg *CodeGen) GenerateStmtIR(stmtNode *Node, last bool, loopBegin, loopEnd string) (string, []string, []string, error) { line := "" setups := []string{} gsetups := []string{} switch stmtNode.Type { case parser.NODE_TYPE_CONTROL_KEYWORD: switch stmtNode.Value.(string) { case "return": line = "ret" if stmtNode.Right != nil { line += " " val, setps, gsetps, _, err := cg.GenerateExprIR(stmtNode.Right) if err != nil { return "", nil, nil, err } line += val setups = append(setups, setps...) gsetups = append(gsetups, gsetps...) } case "break": if loopEnd == "" { return "", nil, nil, shared.NewError(stmtNode.Loc, "used 'break' keyword outside loop") } line = fmt.Sprintf("jmp %s", loopEnd) case "continue": if loopBegin == "" { return "", nil, nil, shared.NewError(stmtNode.Loc, "used 'continue' keyword outside loop") } line = fmt.Sprintf("jmp %s", loopBegin) } case parser.NODE_TYPE_DECLARATION: sz := shared.GetSizeOfType(stmtNode.Right.ExprType) nme := typechecker.IdentToStr(stmtNode.Left) setups = append(setups, fmt.Sprintf("%%%s =l alloc%d %d", nme, sz, sz)) val, setps, gsetps, _, err := cg.GenerateExprIR(stmtNode.Right) if err != nil { return "", nil, nil, err } line += fmt.Sprintf("store%s %s, %%%s", mapTypeToIRType(stmtNode.Right.ExprType), val, nme) setups = append(setups, setps...) gsetups = append(gsetups, gsetps...) case parser.NODE_TYPE_ASSIGNMENT: if stmtNode.Left.Type == parser.NODE_TYPE_IDENTIFIER { nme := typechecker.IdentToStr(stmtNode.Left) val, setps, gsetps, _, err := cg.GenerateExprIR(stmtNode.Right) if err != nil { return "", nil, nil, err } line = fmt.Sprintf("store%s %s, %%%s", mapTypeToIRType(stmtNode.Right.ExprType), val, nme) setups = append(setups, setps...) gsetups = append(gsetups, gsetps...) } else if stmtNode.Left.Type == parser.NODE_TYPE_UNARY_OP && stmtNode.Left.Value.(string) == "*" { val, setps, gsetps, _, err := cg.GenerateExprIR(stmtNode.Right) if err != nil { return "", nil, nil, err } ptrVal, ptrSetps, ptrGSetps, _, err := cg.GenerateExprIR(stmtNode.Left.Right) if err != nil { return "", nil, nil, err } line = fmt.Sprintf("store%s %s, %s", mapTypeToIRType(stmtNode.Right.ExprType), val, ptrVal) setups = append(setups, ptrSetps...) setups = append(setups, setps...) gsetups = append(gsetups, ptrGSetps...) gsetups = append(gsetups, gsetps...) } case parser.NODE_TYPE_FUNCTION_CALL: fname := typechecker.IdentToStr(stmtNode.Value.(*Node)) fsig := cg.funcSigs[fname] line = fmt.Sprintf("call $%s(", fsig.Name) for i, arg := range stmtNode.Children { if i == len(fsig.ArgTypes) { line += "..., " } argType := "" val, setps, gsetps, exprTpe, err := cg.GenerateExprIR(arg) if err != nil { return "", nil, nil, err } if i < len(fsig.ArgTypes) { argType = mapTypeToIRType(fsig.ArgTypes[i].R) } else { argType = exprTpe } line += fmt.Sprintf("%s %s", argType, val) if i != len(stmtNode.Children)-1 { line += ", " } setups = append(setups, setps...) gsetups = append(gsetups, gsetps...) } line += ")" case parser.NODE_TYPE_IF: ifVal := stmtNode.Value.(*parser.IfNodeValue) endLabel := cg.GetTmpLbl() condVal, condSetups, condGSetups, _, err := cg.GenerateExprIR(ifVal.IfBranch.Condition) if err != nil { return "", nil, nil, err } ifLabel := cg.GetTmpLbl() elseCheckLabel := cg.GetTmpLbl() setups = append(setups, condSetups...) gsetups = append(gsetups, condGSetups...) setups = append(setups, fmt.Sprintf("jnz %s, %s, %s", condVal, ifLabel, elseCheckLabel)) ifBlockIR, blockGSetups, ifTerminated, err := cg.GenerateBlockIR(ifVal.IfBranch.Node, loopBegin, loopEnd) if err != nil { return "", nil, nil, err } setups = append(setups, fmt.Sprintf("%s", ifLabel)) setups = append(setups, ifBlockIR) if !ifTerminated { setups = append(setups, fmt.Sprintf("jmp %s", endLabel)) } gsetups = append(gsetups, blockGSetups...) currentCheckLabel := elseCheckLabel for _, elseIf := range ifVal.ElseIfBranches { elseIfCondVal, elseIfCondSetups, elseIfCondGSetups, _, err := cg.GenerateExprIR(elseIf.Condition) if err != nil { return "", nil, nil, err } elseIfLabel := cg.GetTmpLbl() nextCheckLabel := cg.GetTmpLbl() setups = append(setups, fmt.Sprintf("%s", currentCheckLabel)) setups = append(setups, elseIfCondSetups...) gsetups = append(gsetups, elseIfCondGSetups...) setups = append(setups, fmt.Sprintf("jnz %s, %s, %s", elseIfCondVal, elseIfLabel, nextCheckLabel)) elseIfBlockIR, blockGSetups, elseIfTerminated, err := cg.GenerateBlockIR(elseIf.Node, loopBegin, loopEnd) if err != nil { return "", nil, nil, err } setups = append(setups, fmt.Sprintf("%s", elseIfLabel)) setups = append(setups, elseIfBlockIR) if !elseIfTerminated { setups = append(setups, fmt.Sprintf("jmp %s", endLabel)) } gsetups = append(gsetups, blockGSetups...) currentCheckLabel = nextCheckLabel } if ifVal.ElseBranch != nil { elseLabel := cg.GetTmpLbl() setups = append(setups, fmt.Sprintf("%s", currentCheckLabel)) setups = append(setups, fmt.Sprintf("jmp %s", elseLabel)) elseBlockIR, blockGSetups, elseTerminated, err := cg.GenerateBlockIR(ifVal.ElseBranch.Node, loopBegin, loopEnd) if err != nil { return "", nil, nil, err } setups = append(setups, fmt.Sprintf("%s", elseLabel)) setups = append(setups, elseBlockIR) if !elseTerminated { setups = append(setups, fmt.Sprintf("jmp %s", endLabel)) } gsetups = append(gsetups, blockGSetups...) } else { setups = append(setups, fmt.Sprintf("%s", currentCheckLabel)) setups = append(setups, fmt.Sprintf("jmp %s", endLabel)) } setups = append(setups, fmt.Sprintf("%s", endLabel)) case parser.NODE_TYPE_FOR: value := stmtNode.Value.(*parser.ForLoopValue) beginLbl := cg.GetTmpLbl() bodyLbl := cg.GetTmpLbl() endLbl := cg.GetTmpLbl() line = beginLbl + "\n" body, blockGSetups, _, err := cg.GenerateBlockIR(value.Body, beginLbl, endLbl) if err != nil { return "", nil, nil, err } if len(value.ExprsOrStmts) == 1 { cond, setps, gsetps, _, err := cg.GenerateExprIR(value.ExprsOrStmts[0]) if err != nil { return "", nil, nil, err } for _, stp := range setps { line += stp + "\n" } gsetups = append(gsetups, gsetps...) line += fmt.Sprintf("jnz %s %s, %s\n", cond, bodyLbl, endLbl) line += bodyLbl + "\n" + body + "\n" line += fmt.Sprintf("jmp %s\n", beginLbl) } else if len(value.ExprsOrStmts) == 3 { l, setps, gsetps, err := cg.GenerateStmtIR(value.ExprsOrStmts[0], last, beginLbl, endLbl) if err != nil { return "", nil, nil, err } setups = append(setups, setps...) setups = append(setups, l) gsetups = append(gsetups, gsetps...) cond, setps, gsetps, _, err := cg.GenerateExprIR(value.ExprsOrStmts[1]) if err != nil { return "", nil, nil, err } for _, stp := range setps { line += stp + "\n" } line += fmt.Sprintf("jnz %s, %s, %s\n", cond, bodyLbl, endLbl) line += bodyLbl + "\n" + body + "\n" reass, setps, gsetps, err := cg.GenerateStmtIR(value.ExprsOrStmts[2], last, beginLbl, endLbl) if err != nil { return "", nil, nil, err } for _, stp := range setps { line += stp + "\n" } gsetups = append(gsetups, gsetps...) line += reass + "\n" line += fmt.Sprintf("jmp %s\n", beginLbl) } else { line += bodyLbl + "\n" + body + "\n" } line += endLbl gsetups = append(gsetups, blockGSetups...) default: return "", nil, nil, shared.NewError(stmtNode.Loc, "unsupported statement %s", stmtNode.Type) } return line, setups, gsetups, nil } func (cg *CodeGen) GenerateExprIR(exprNode *Node) (string, []string, []string, string, error) { val := "" setups := []string{} gsetups := []string{} tpe := mapTypeToIRType(exprNode.ExprType) switch exprNode.Type { case parser.NODE_TYPE_IDENTIFIER: irt := mapTypeToIRType(exprNode.ExprType) tnme := cg.GetTmpVar() setups = append(setups, fmt.Sprintf("%s =%s load%s %%%s", tnme, irt, irt, typechecker.IdentToStr(exprNode))) val = tnme case parser.NODE_TYPE_NUMBER_LITERAL: val = exprNode.Value.(string) case parser.NODE_TYPE_CHAR_LITERAL: val = strconv.Itoa(int(exprNode.Value.(byte))) case parser.NODE_TYPE_STRING_LITERAL: nme := cg.GetCnst() val = nme gsetups = append(gsetups, fmt.Sprintf("data %s = { b \"%s\", b 0 }", nme, strings.NewReplacer("\\", `\\`, "\"", `\"`, "\n", `\n`, "\r", `\r`, "\t", `\t`, "\b", `\b`, "\f", `\f`, "\v", `\v`, "\a", `\a`, string(rune(0)), `\0`, ).Replace(exprNode.Value.(string)))) case parser.NODE_TYPE_BOOL_LITERAL: if exprNode.Value.(string) == "true" { val = "1" } else { val = "0" } case parser.NODE_TYPE_FUNCTION_CALL: fname := typechecker.IdentToStr(exprNode.Value.(*Node)) fsig := cg.funcSigs[fname] val = cg.GetTmpVar() tpe = mapTypeToIRType(fsig.RetType) setup := fmt.Sprintf("%s =%s call $%s(", val, tpe, fname) for i, arg := range exprNode.Children { if i == len(fsig.ArgTypes) { setup += "..., " } argType := "" v, st, gst, exprTpe, err := cg.GenerateExprIR(arg) if err != nil { return "", nil, nil, "", err } if i < len(fsig.ArgTypes) { argType = mapTypeToIRType(fsig.ArgTypes[i].R) } else { argType = exprTpe } setup += fmt.Sprintf("%s %s", argType, v) if i != len(exprNode.Children)-1 { setup += ", " } setups = append(setups, st...) gsetups = append(gsetups, gst...) } setup += ")" setups = append(setups, setup) case parser.NODE_TYPE_BINARY_OP: tpe = mapTypeToIRType(exprNode.Left.ExprType) op := "" switch exprNode.Value.(string) { case "+": op = "add" case "-": op = "sub" case "*": op = "mul" case "/": if shared.IsUnsignedType(exprNode.Left.ExprType) { op = "udiv" } else { op = "div" } case "%": if shared.IsUnsignedType(exprNode.Left.ExprType) { op = "urem" } else { op = "rem" } case "==": op = "ceq" + tpe case "!=": op = "cne" + tpe case "<": if shared.IsUnsignedType(exprNode.Left.ExprType) { op = "cult" + tpe } else { op = "cslt" + tpe } case ">": if shared.IsUnsignedType(exprNode.Left.ExprType) { op = "cugt" + tpe } else { op = "csgt" + tpe } case "<=": if shared.IsUnsignedType(exprNode.Left.ExprType) { op = "cule" + tpe } else { op = "csle" + tpe } case ">=": if shared.IsUnsignedType(exprNode.Left.ExprType) { op = "cuge" + tpe } else { op = "csge" + tpe } } nm := cg.GetTmpVar() val = fmt.Sprintf("%s", nm) v1, st1, gst1, _, err := cg.GenerateExprIR(exprNode.Left) if err != nil { return "", nil, nil, "", err } v2, st2, gst2, _, err := cg.GenerateExprIR(exprNode.Right) if err != nil { return "", nil, nil, "", err } setup := fmt.Sprintf("%s =%s %s %s, %s", val, tpe, op, v1, v2) setups = append(setups, st1...) setups = append(setups, st2...) gsetups = append(gsetups, gst1...) gsetups = append(gsetups, gst2...) setups = append(setups, setup) case parser.NODE_TYPE_UNARY_OP: nm := cg.GetTmpVar() val = fmt.Sprintf("%s", nm) switch exprNode.Value.(string) { case "&": if exprNode.Right.Type == parser.NODE_TYPE_IDENTIFIER { setups = append(setups, fmt.Sprintf("%s =l copy %%%s", val, typechecker.IdentToStr(exprNode.Right))) } else { return "", nil, nil, "", shared.NewError(exprNode.Loc, "cannot take address of non-variable expression") } case "*": if exprNode.Right.Type == parser.NODE_TYPE_IDENTIFIER { nm2 := cg.GetTmpVar() val2 := fmt.Sprintf("%s", nm2) setups = append(setups, fmt.Sprintf("%s =l loadl %%%s", val2, typechecker.IdentToStr(exprNode.Right))) setups = append(setups, fmt.Sprintf("%s =%s load%s %s", val, mapTypeToIRType(exprNode.ExprType), mapTypeToIRType(exprNode.ExprType), val2)) } else { return "", nil, nil, "", shared.NewError(exprNode.Loc, "cannot dereference non-variable expression") } default: setup := "" v, st, gst, _, err := cg.GenerateExprIR(exprNode.Right) if err != nil { return "", nil, nil, "", err } switch exprNode.Value.(string) { case "-": setup = fmt.Sprintf("%s =%s neg %s", val, mapTypeToIRType(exprNode.Right.ExprType), v) case "not": tpe := mapTypeToIRType(exprNode.Right.ExprType) setup = fmt.Sprintf("%s =%s cne%s %s, 1", val, tpe, tpe, v) } setups = append(setups, st...) gsetups = append(gsetups, gst...) setups = append(setups, setup) } case parser.NODE_TYPE_IF_EXPR: ifEVal := exprNode.Value.(*parser.IfNodeValue) endLabel := cg.GetTmpLbl() nm := cg.GetTmpVar() val = fmt.Sprintf("%s", nm) tpe := mapTypeToIRType(exprNode.ExprType) condVal, condSetups, condGSetups, _, err := cg.GenerateExprIR(ifEVal.IfBranch.Condition) if err != nil { return "", nil, nil, "", err } ifLabel := cg.GetTmpLbl() elseCheckLabel := cg.GetTmpLbl() setups = append(setups, condSetups...) gsetups = append(gsetups, condGSetups...) setups = append(setups, fmt.Sprintf("jnz %s, %s, %s", condVal, ifLabel, elseCheckLabel)) ifVal, ifSetups, ifGSetups, _, err := cg.GenerateExprIR(ifEVal.IfBranch.Node) if err != nil { return "", nil, nil, "", err } ifBlockCode := fmt.Sprintf("%s", ifLabel) for _, s := range ifSetups { ifBlockCode += "\n" + s } gsetups = append(gsetups, ifGSetups...) ifBlockCode += fmt.Sprintf("\n%s =%s copy %s", nm, tpe, ifVal) ifBlockCode += fmt.Sprintf("\njmp %s", endLabel) setups = append(setups, ifBlockCode) currentCheckLabel := elseCheckLabel for _, elseIf := range ifEVal.ElseIfBranches { elseIfCondVal, elseIfCondSetups, elseIfCondGSetups, _, err := cg.GenerateExprIR(elseIf.Condition) if err != nil { return "", nil, nil, "", err } elseIfLabel := cg.GetTmpLbl() nextCheckLabel := cg.GetTmpLbl() setups = append(setups, fmt.Sprintf("%s", currentCheckLabel)) setups = append(setups, elseIfCondSetups...) gsetups = append(gsetups, elseIfCondGSetups...) setups = append(setups, fmt.Sprintf("jnz %s, %s, %s", elseIfCondVal, elseIfLabel, nextCheckLabel)) elseIfVal, elseIfSetups, elseIfGSetups, _, err := cg.GenerateExprIR(elseIf.Node) if err != nil { return "", nil, nil, "", err } elseIfBlockCode := fmt.Sprintf("%s", elseIfLabel) for _, s := range elseIfSetups { elseIfBlockCode += "\n" + s } gsetups = append(gsetups, elseIfGSetups...) elseIfBlockCode += fmt.Sprintf("\n%s =%s copy %s", nm, tpe, elseIfVal) elseIfBlockCode += fmt.Sprintf("\njmp %s", endLabel) setups = append(setups, elseIfBlockCode) currentCheckLabel = nextCheckLabel } elseLabel := cg.GetTmpLbl() setups = append(setups, fmt.Sprintf("%s", currentCheckLabel)) setups = append(setups, fmt.Sprintf("jmp %s", elseLabel)) elseVal, elseSetups, elseGSetups, _, err := cg.GenerateExprIR(ifEVal.ElseBranch.Node) if err != nil { return "", nil, nil, "", err } elseBlockCode := fmt.Sprintf("%s", elseLabel) for _, s := range elseSetups { elseBlockCode += "\n" + s } gsetups = append(gsetups, elseGSetups...) elseBlockCode += fmt.Sprintf("\n%s =%s copy %s", nm, tpe, elseVal) elseBlockCode += fmt.Sprintf("\njmp %s", endLabel) setups = append(setups, elseBlockCode) setups = append(setups, fmt.Sprintf("%s", endLabel)) case parser.NODE_TYPE_CAST: targetTypeIdent := exprNode.Left.Value.(string) targetType, _ := cg.typeTable.Lookup(targetTypeIdent) targetIRType := mapTypeToIRType(targetType) sourceType := exprNode.Right.ExprType sourceIRType := mapTypeToIRType(sourceType) sourceVal, sourceSetups, sourceGSetups, _, err := cg.GenerateExprIR(exprNode.Right) if err != nil { return "", nil, nil, "", err } setups = append(setups, sourceSetups...) gsetups = append(gsetups, sourceGSetups...) currentVal := sourceVal if sourceIRType != targetIRType { tmpVar := cg.GetTmpVar() if sourceIRType == "w" && targetIRType == "l" { if shared.IsUnsignedType(sourceType) { setups = append(setups, fmt.Sprintf("%s =l extuw %s", tmpVar, currentVal)) } else { setups = append(setups, fmt.Sprintf("%s =l extsw %s", tmpVar, currentVal)) } currentVal = tmpVar } else if sourceIRType == "l" && targetIRType == "w" { setups = append(setups, fmt.Sprintf("%s =w copy %s", tmpVar, currentVal)) currentVal = tmpVar } else { return "", nil, nil, "", shared.NewError(exprNode.Loc, "unsupported cast from %s to %s", sourceIRType, targetIRType) } } mask, ext := getTruncateMaskAndExt(targetType) if mask != 0 { tmpTrunc := cg.GetTmpVar() setups = append(setups, fmt.Sprintf("%s =w and %s, %d", tmpTrunc, currentVal, mask)) currentVal = tmpTrunc if ext != "" { tmpExt := cg.GetTmpVar() setups = append(setups, fmt.Sprintf("%s =w %s %s", tmpExt, ext, currentVal)) currentVal = tmpExt } } val = currentVal default: return "", nil, nil, "", shared.NewError(exprNode.Loc, "unsupported expression %s", exprNode.Type) } mask, ext := getTruncateMaskAndExt(exprNode.ExprType) if mask != 0 { truncTmp := cg.GetTmpVar() setups = append(setups, fmt.Sprintf("%s =w and %s, %d", truncTmp, val, mask)) val = truncTmp if ext != "" { extTmp := cg.GetTmpVar() setups = append(setups, fmt.Sprintf("%s =w %s %s", extTmp, ext, val)) val = extTmp } } return val, setups, gsetups, tpe, nil } func mapTypeToIRType(t Type) string { switch t { case shared.PRIMITIVE_VOID: return "" case shared.PRIMITIVE_BOOL: return "w" case shared.PRIMITIVE_CHAR: return "w" case shared.PRIMITIVE_CSTRING: return "l" case shared.PRIMITIVE_I8, shared.PRIMITIVE_U8: return "w" case shared.PRIMITIVE_I16, shared.PRIMITIVE_U16: return "w" case shared.PRIMITIVE_I32, shared.PRIMITIVE_U32: return "w" case shared.PRIMITIVE_I64, shared.PRIMITIVE_U64: return "l" case shared.PRIMITIVE_UNTYPED_INT: return "l" default: if t.IsStruct() { return "l" } if t.IsPointer() { return "l" } panic("unknown type") } } func getTruncateMaskAndExt(t Type) (mask int, extOp string) { switch t { case shared.PRIMITIVE_U8: return 0xFF, "" case shared.PRIMITIVE_I8: return 0xFF, "extsb" case shared.PRIMITIVE_U16: return 0xFFFF, "" case shared.PRIMITIVE_I16: return 0xFFFF, "extsh" default: return 0, "" } }