codegen_helpers.go
Go
1package codegen
2
3import (
4 "github.com/xplshn/gbc/pkg/ast"
5 "github.com/xplshn/gbc/pkg/config"
6 "github.com/xplshn/gbc/pkg/ir"
7 "github.com/xplshn/gbc/pkg/token"
8 "github.com/xplshn/gbc/pkg/util"
9)
10
11func (ctx *Context) codegenIdent(node *ast.Node) (ir.Value, bool) {
12 name := node.Data.(ast.IdentNode).Name
13 sym := ctx.findSymbol(name)
14
15 if sym == nil {
16 util.Warn(ctx.cfg, config.WarnImplicitDecl, node.Tok, "Implicit declaration of function '%s'", name)
17 sym = ctx.addSymbol(name, symFunc, ast.TypeUntyped, false, node)
18 return sym.IRVal, false
19 }
20
21 switch sym.Type {
22 case symFunc: return sym.IRVal, false
23 case symExtrn:
24 isCall := node.Parent != nil && node.Parent.Type == ast.FuncCall && node.Parent.Data.(ast.FuncCallNode).FuncExpr == node
25 if isCall {
26 return sym.IRVal, false
27 }
28 ctx.prog.ExtrnVars[name] = true
29 res := ctx.newTemp()
30 ctx.addInstr(&ir.Instruction{Op: ir.OpLoad, Typ: ir.TypePtr, Result: res, Args: []ir.Value{sym.IRVal}})
31 return res, false
32 }
33
34 isArr := sym.IsVector || (sym.BxType != nil && sym.BxType.Kind == ast.TYPE_ARRAY)
35 if isArr {
36 isParam := false
37 if sym.Node != nil && sym.Node.Parent != nil && sym.Node.Parent.Type == ast.FuncDecl {
38 funcDecl := sym.Node.Parent.Data.(ast.FuncDeclNode)
39 for _, p := range funcDecl.Params {
40 if p == sym.Node {
41 isParam = true
42 break
43 }
44 }
45 }
46 _, isLocal := sym.IRVal.(*ir.Temporary)
47 if isLocal {
48 isDopeVector := sym.IsVector && (sym.BxType == nil || sym.BxType.Kind == ast.TYPE_UNTYPED)
49 if isParam || isDopeVector {
50 return ctx.genLoad(sym.IRVal, sym.BxType), false
51 }
52 }
53 return sym.IRVal, false
54 }
55
56 if sym.BxType != nil && sym.BxType.Kind == ast.TYPE_STRUCT {
57 return sym.IRVal, false
58 }
59
60 return ctx.genLoad(sym.IRVal, sym.BxType), false
61}
62
63func (ctx *Context) isIntegerType(t *ast.BxType) bool {
64 return t != nil && (t.Kind == ast.TYPE_PRIMITIVE || t.Kind == ast.TYPE_LITERAL_INT || t.Kind == ast.TYPE_ENUM)
65}
66
67func (ctx *Context) isFloatType(t *ast.BxType) bool {
68 return t != nil && (t.Kind == ast.TYPE_FLOAT || t.Kind == ast.TYPE_LITERAL_FLOAT)
69}
70
71// getActualOperandType returns the IR type that will be used when loading this operand
72// This looks at the original declaration type, not the type-checker promoted type
73func (ctx *Context) getActualOperandType(node *ast.Node) ir.Type {
74 switch node.Type {
75 case ast.Ident:
76 // For identifiers, use the symbol's original declared type
77 name := node.Data.(ast.IdentNode).Name
78 if sym := ctx.findSymbol(name); sym != nil && sym.BxType != nil {
79 return ir.GetType(sym.BxType, ctx.wordSize)
80 }
81 case ast.FuncCall:
82 // For function calls, use the function's return type
83 d := node.Data.(ast.FuncCallNode)
84 if d.FuncExpr.Type == ast.Ident {
85 funcName := d.FuncExpr.Data.(ast.IdentNode).Name
86 if sym := ctx.findSymbol(funcName); sym != nil && sym.BxType != nil {
87 return ir.GetType(sym.BxType, ctx.wordSize)
88 }
89 }
90 }
91 // Fallback to the promoted type
92 return ir.GetType(node.Typ, ctx.wordSize)
93}
94
95func (ctx *Context) codegenAssign(node *ast.Node) (ir.Value, bool) {
96 d := node.Data.(ast.AssignNode)
97
98 // Resolve named struct types to their actual definitions
99 lhsType := d.Lhs.Typ
100 if lhsType != nil && lhsType.Kind != ast.TYPE_STRUCT && lhsType.Name != "" {
101 if typeSym := ctx.findTypeSymbol(lhsType.Name); typeSym != nil && typeSym.BxType.Kind == ast.TYPE_STRUCT {
102 lhsType = typeSym.BxType
103 }
104 }
105
106 if lhsType != nil && lhsType.Kind == ast.TYPE_STRUCT {
107 if d.Op != token.Eq {
108 util.Error(node.Tok, "Compound assignment operators are not supported for structs")
109 return nil, false
110 }
111 lvalAddr := ctx.codegenLvalue(d.Lhs)
112 rvalPtr, _ := ctx.codegenExpr(d.Rhs)
113 size := ctx.getSizeof(lhsType)
114 ctx.addInstr(&ir.Instruction{
115 Op: ir.OpBlit,
116 Args: []ir.Value{rvalPtr, lvalAddr, &ir.Const{Value: size}},
117 })
118 return lvalAddr, false
119 }
120
121 lvalAddr := ctx.codegenLvalue(d.Lhs)
122 var rval ir.Value
123
124 if d.Op == token.Eq {
125 rval, _ = ctx.codegenExpr(d.Rhs)
126 if d.Lhs.Typ != nil && d.Rhs.Typ != nil && d.Lhs.Typ.Kind == ast.TYPE_FLOAT && ctx.isIntegerType(d.Rhs.Typ) {
127 castRval := ctx.newTemp()
128 var convOp ir.Op
129 if ctx.getSizeof(d.Rhs.Typ) == 8 {
130 convOp = ir.OpSLToF
131 } else {
132 convOp = ir.OpSWToF
133 }
134 ctx.addInstr(&ir.Instruction{
135 Op: convOp,
136 Typ: ir.GetType(d.Lhs.Typ, ctx.wordSize),
137 Result: castRval,
138 Args: []ir.Value{rval},
139 })
140 rval = castRval
141 }
142 } else {
143 currentLvalVal := ctx.genLoad(lvalAddr, d.Lhs.Typ)
144 rhsVal, _ := ctx.codegenExpr(d.Rhs)
145 op, typ := getBinaryOpAndType(d.Op, d.Lhs.Typ, ctx.wordSize)
146 rval = ctx.newTemp()
147 ctx.addInstr(&ir.Instruction{Op: op, Typ: typ, Result: rval, Args: []ir.Value{currentLvalVal, rhsVal}})
148 }
149
150 ctx.genStore(lvalAddr, rval, d.Lhs.Typ)
151 return rval, false
152}
153
154func (ctx *Context) codegenMultiAssign(node *ast.Node) (ir.Value, bool) {
155 d := node.Data.(ast.MultiAssignNode)
156
157 // Only support simple '=' assignment for multi-assignment
158 if d.Op != token.Eq {
159 util.Error(node.Tok, "Compound assignment operators are not supported for multi-assignment")
160 return nil, false
161 }
162
163 // Evaluate all rhs expressions first to avoid dependencies
164 var rvals []ir.Value
165 for _, rhs := range d.Rhs {
166 rval, _ := ctx.codegenExpr(rhs)
167 rvals = append(rvals, rval)
168 }
169
170 // Then assign to all lhs expressions
171 for i, lhs := range d.Lhs {
172 lvalAddr := ctx.codegenLvalue(lhs)
173 rval := rvals[i]
174
175 // Handle type conversions if needed (similar to single assignment)
176 if lhs.Typ != nil && d.Rhs[i].Typ != nil && lhs.Typ.Kind == ast.TYPE_FLOAT && ctx.isIntegerType(d.Rhs[i].Typ) {
177 castRval := ctx.newTemp()
178 var convOp ir.Op
179 if ctx.getSizeof(d.Rhs[i].Typ) == 8 {
180 convOp = ir.OpSLToF
181 } else {
182 convOp = ir.OpSWToF
183 }
184 ctx.addInstr(&ir.Instruction{
185 Op: convOp,
186 Typ: ir.GetType(lhs.Typ, ctx.wordSize),
187 Result: castRval,
188 Args: []ir.Value{rval},
189 })
190 rval = castRval
191 }
192
193 ctx.genStore(lvalAddr, rval, lhs.Typ)
194 }
195
196 // Return the last assigned value
197 if len(rvals) > 0 {
198 return rvals[len(rvals)-1], false
199 }
200 return nil, false
201}
202
203func (ctx *Context) codegenBinaryOp(node *ast.Node) (ir.Value, bool) {
204 d := node.Data.(ast.BinaryOpNode)
205 if d.Op == token.OrOr || d.Op == token.AndAnd {
206 res := ctx.newTemp()
207 trueL, falseL, endL := ctx.newLabel(), ctx.newLabel(), ctx.newLabel()
208
209 ctx.codegenLogicalCond(node, trueL, falseL)
210
211 ctx.startBlock(trueL)
212 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{endL}})
213
214 ctx.startBlock(falseL)
215 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{endL}})
216
217 ctx.startBlock(endL)
218 wordType := ir.GetType(nil, ctx.wordSize)
219 ctx.addInstr(&ir.Instruction{
220 Op: ir.OpPhi,
221 Typ: wordType,
222 Result: res,
223 Args: []ir.Value{
224 trueL, &ir.Const{Value: 1},
225 falseL, &ir.Const{Value: 0},
226 },
227 })
228 return res, false
229 }
230
231 l, _ := ctx.codegenExpr(d.Left)
232 r, _ := ctx.codegenExpr(d.Right)
233 res := ctx.newTemp()
234 op, resultIrType := getBinaryOpAndType(d.Op, node.Typ, ctx.wordSize)
235
236 isComparison := op >= ir.OpCEq && op <= ir.OpCGe
237 isFloatComparison := false
238 if isComparison && (ctx.isFloatType(d.Left.Typ) || ctx.isFloatType(d.Right.Typ)) {
239 isFloatComparison = true
240 }
241
242 if ctx.isFloatType(node.Typ) || isFloatComparison {
243 floatType := resultIrType
244 if isFloatComparison {
245 if ctx.isFloatType(d.Left.Typ) {
246 floatType = ir.GetType(d.Left.Typ, ctx.wordSize)
247 } else {
248 floatType = ir.GetType(d.Right.Typ, ctx.wordSize)
249 }
250 }
251
252 if !ctx.isFloatType(d.Left.Typ) {
253 castL := ctx.newTemp()
254 var convOp ir.Op
255 if ctx.getSizeof(d.Left.Typ) == 8 {
256 convOp = ir.OpSLToF
257 } else {
258 convOp = ir.OpSWToF
259 }
260 ctx.addInstr(&ir.Instruction{Op: convOp, Typ: floatType, Result: castL, Args: []ir.Value{l}})
261 l = castL
262 }
263 if !ctx.isFloatType(d.Right.Typ) {
264 castR := ctx.newTemp()
265 var convOp ir.Op
266 if ctx.getSizeof(d.Right.Typ) == 8 {
267 convOp = ir.OpSLToF
268 } else {
269 convOp = ir.OpSWToF
270 }
271 ctx.addInstr(&ir.Instruction{Op: convOp, Typ: floatType, Result: castR, Args: []ir.Value{r}})
272 r = castR
273 }
274 if l_const, ok := l.(*ir.Const); ok {
275 l = &ir.FloatConst{Value: float64(l_const.Value), Typ: floatType}
276 }
277 if r_const, ok := r.(*ir.Const); ok {
278 r = &ir.FloatConst{Value: float64(r_const.Value), Typ: floatType}
279 }
280 }
281
282 // Handle integer type conversions - ensure both operands have compatible types for QBE
283 if ctx.isIntegerType(node.Typ) && !isFloatComparison {
284 // For QBE compatibility, we need to look at the actual declaration types of the operands
285 // rather than the promoted types from the type checker
286 actualLeftType := ctx.getActualOperandType(d.Left)
287 actualRightType := ctx.getActualOperandType(d.Right)
288
289 // Use actual types for conversion logic
290 if actualLeftType != resultIrType {
291 castL := ctx.newTemp()
292 var convOp ir.Op = ir.OpCast
293 if actualLeftType < resultIrType {
294 // Extending to larger size
295 switch actualLeftType {
296 case ir.TypeB: convOp = ir.OpExtUB
297 case ir.TypeH: convOp = ir.OpExtUH
298 case ir.TypeW: convOp = ir.OpExtSW
299 }
300 }
301 ctx.addInstr(&ir.Instruction{Op: convOp, Typ: resultIrType, OperandType: actualLeftType, Result: castL, Args: []ir.Value{l}})
302 l = castL
303 }
304
305 if actualRightType != resultIrType {
306 castR := ctx.newTemp()
307 var convOp ir.Op = ir.OpCast
308 if actualRightType < resultIrType {
309 // Extending to larger size
310 switch actualRightType {
311 case ir.TypeB: convOp = ir.OpExtUB
312 case ir.TypeH: convOp = ir.OpExtUH
313 case ir.TypeW: convOp = ir.OpExtSW
314 }
315 }
316 ctx.addInstr(&ir.Instruction{Op: convOp, Typ: resultIrType, OperandType: actualRightType, Result: castR, Args: []ir.Value{r}})
317 r = castR
318 }
319 }
320
321 var operandIrType ir.Type
322 if isComparison {
323 if ctx.isFloatType(d.Left.Typ) || ctx.isFloatType(d.Right.Typ) {
324 if ctx.isFloatType(d.Left.Typ) {
325 operandIrType = ir.GetType(d.Left.Typ, ctx.wordSize)
326 } else {
327 operandIrType = ir.GetType(d.Right.Typ, ctx.wordSize)
328 }
329 } else {
330 operandIrType = ir.GetType(d.Left.Typ, ctx.wordSize)
331 }
332 } else {
333 operandIrType = resultIrType
334 }
335
336 ctx.addInstr(&ir.Instruction{
337 Op: op,
338 Typ: resultIrType,
339 OperandType: operandIrType,
340 Result: res,
341 Args: []ir.Value{l, r},
342 })
343 return res, false
344}
345
346func (ctx *Context) codegenUnaryOp(node *ast.Node) (ir.Value, bool) {
347 d := node.Data.(ast.UnaryOpNode)
348 res := ctx.newTemp()
349 val, _ := ctx.codegenExpr(d.Expr)
350 valType := ir.GetType(d.Expr.Typ, ctx.wordSize)
351 isFloat := ctx.isFloatType(d.Expr.Typ)
352
353 switch d.Op {
354 case token.Minus:
355 if isFloat {
356 ctx.addInstr(&ir.Instruction{Op: ir.OpNegF, Typ: valType, Result: res, Args: []ir.Value{val}})
357 } else {
358 ctx.addInstr(&ir.Instruction{Op: ir.OpSub, Typ: valType, Result: res, Args: []ir.Value{&ir.Const{Value: 0}, val}})
359 }
360 case token.Plus: return val, false
361 case token.Not:
362 wordType := ir.GetType(nil, ctx.wordSize)
363 ctx.addInstr(&ir.Instruction{Op: ir.OpCEq, Typ: wordType, OperandType: valType, Result: res, Args: []ir.Value{val, &ir.Const{Value: 0}}})
364 case token.Complement:
365 wordType := ir.GetType(nil, ctx.wordSize)
366 ctx.addInstr(&ir.Instruction{Op: ir.OpXor, Typ: wordType, Result: res, Args: []ir.Value{val, &ir.Const{Value: -1}}})
367 case token.Inc, token.Dec:
368 lvalAddr := ctx.codegenLvalue(d.Expr)
369 op := map[token.Type]ir.Op{token.Inc: ir.OpAdd, token.Dec: ir.OpSub}[d.Op]
370 if isFloat {
371 op = map[token.Type]ir.Op{token.Inc: ir.OpAddF, token.Dec: ir.OpSubF}[d.Op]
372 }
373 currentVal := ctx.genLoad(lvalAddr, d.Expr.Typ)
374 oneConst := ir.Value(&ir.Const{Value: 1})
375 if isFloat {
376 oneConst = &ir.FloatConst{Value: 1.0, Typ: valType}
377 }
378 ctx.addInstr(&ir.Instruction{Op: op, Typ: valType, Result: res, Args: []ir.Value{currentVal, oneConst}})
379 ctx.genStore(lvalAddr, res, d.Expr.Typ)
380 default:
381 util.Error(node.Tok, "Unsupported unary operator")
382 }
383 return res, false
384}
385
386func (ctx *Context) codegenPostfixOp(node *ast.Node) (ir.Value, bool) {
387 d := node.Data.(ast.PostfixOpNode)
388 lvalAddr := ctx.codegenLvalue(d.Expr)
389 res := ctx.genLoad(lvalAddr, d.Expr.Typ)
390
391 newVal := ctx.newTemp()
392 valType := ir.GetType(d.Expr.Typ, ctx.wordSize)
393 isFloat := ctx.isFloatType(d.Expr.Typ)
394
395 op := map[token.Type]ir.Op{token.Inc: ir.OpAdd, token.Dec: ir.OpSub}[d.Op]
396 if isFloat {
397 op = map[token.Type]ir.Op{token.Inc: ir.OpAddF, token.Dec: ir.OpSubF}[d.Op]
398 }
399
400 oneConst := ir.Value(&ir.Const{Value: 1})
401 if isFloat {
402 oneConst = &ir.FloatConst{Value: 1.0, Typ: valType}
403 }
404
405 ctx.addInstr(&ir.Instruction{Op: op, Typ: valType, Result: newVal, Args: []ir.Value{res, oneConst}})
406 ctx.genStore(lvalAddr, newVal, d.Expr.Typ)
407 return res, false
408}
409
410func (ctx *Context) codegenIndirection(node *ast.Node) (ir.Value, bool) {
411 exprNode := node.Data.(ast.IndirectionNode).Expr
412 addr, _ := ctx.codegenExpr(exprNode)
413
414 // Resolve named struct types to their actual definitions
415 nodeType := node.Typ
416 if nodeType != nil && nodeType.Kind != ast.TYPE_STRUCT && nodeType.Name != "" {
417 if typeSym := ctx.findTypeSymbol(nodeType.Name); typeSym != nil && typeSym.BxType.Kind == ast.TYPE_STRUCT {
418 nodeType = typeSym.BxType
419 }
420 }
421
422 if nodeType != nil && nodeType.Kind == ast.TYPE_STRUCT {
423 return addr, false
424 }
425
426 loadType := node.Typ
427 if !ctx.isTypedPass && exprNode.Type == ast.Ident {
428 if sym := ctx.findSymbol(exprNode.Data.(ast.IdentNode).Name); sym != nil && sym.IsByteArray {
429 loadType = ast.TypeByte
430 }
431 }
432 return ctx.genLoad(addr, loadType), false
433}
434
435func (ctx *Context) codegenSubscriptAddr(node *ast.Node) ir.Value {
436 d := node.Data.(ast.SubscriptNode)
437 arrayPtr, _ := ctx.codegenExpr(d.Array)
438 indexVal, _ := ctx.codegenExpr(d.Index)
439
440 var scale int64 = int64(ctx.wordSize)
441 if d.Array.Typ != nil {
442 if d.Array.Typ.Kind == ast.TYPE_POINTER || d.Array.Typ.Kind == ast.TYPE_ARRAY {
443 if d.Array.Typ.Base != nil {
444 scale = ctx.getSizeof(d.Array.Typ.Base)
445 }
446 }
447 } else if !ctx.isTypedPass && d.Array.Type == ast.Ident {
448 if sym := ctx.findSymbol(d.Array.Data.(ast.IdentNode).Name); sym != nil && sym.IsByteArray {
449 scale = 1
450 }
451 }
452
453 var scaledIndex ir.Value = indexVal
454 if scale > 1 {
455 scaledIndex = ctx.newTemp()
456 ctx.addInstr(&ir.Instruction{
457 Op: ir.OpMul,
458 Typ: ir.GetType(nil, ctx.wordSize),
459 Result: scaledIndex,
460 Args: []ir.Value{indexVal, &ir.Const{Value: scale}},
461 })
462 }
463
464 resultAddr := ctx.newTemp()
465 ctx.addInstr(&ir.Instruction{
466 Op: ir.OpAdd,
467 Typ: ir.GetType(nil, ctx.wordSize),
468 Result: resultAddr,
469 Args: []ir.Value{arrayPtr, scaledIndex},
470 })
471 return resultAddr
472}
473
474func (ctx *Context) codegenAddressOf(node *ast.Node) (ir.Value, bool) {
475 lvalNode := node.Data.(ast.AddressOfNode).LValue
476 if lvalNode.Type == ast.Ident {
477 name := lvalNode.Data.(ast.IdentNode).Name
478 if sym := ctx.findSymbol(name); sym != nil {
479 isTypedArray := sym.BxType != nil && sym.BxType.Kind == ast.TYPE_ARRAY
480 if sym.Type == symFunc || isTypedArray {
481 return sym.IRVal, false
482 }
483 if sym.IsVector {
484 res, _ := ctx.codegenExpr(lvalNode)
485 return res, false
486 }
487 }
488 }
489 return ctx.codegenLvalue(lvalNode), false
490}
491
492func (ctx *Context) codegenFuncCall(node *ast.Node) (ir.Value, bool) {
493 d := node.Data.(ast.FuncCallNode)
494 if d.FuncExpr.Type == ast.Ident {
495 name := d.FuncExpr.Data.(ast.IdentNode).Name
496 if sym := ctx.findSymbol(name); sym != nil && sym.Type == symVar && !sym.IsVector {
497 util.Error(d.FuncExpr.Tok, "'%s' is a variable but is used as a function", name)
498 }
499 }
500
501 funcVal, _ := ctx.codegenExpr(d.FuncExpr)
502
503 // Get function signature for type checking
504 var expectedParamTypes []*ast.BxType
505 isVariadic := false
506
507 if d.FuncExpr.Type == ast.Ident {
508 name := d.FuncExpr.Data.(ast.IdentNode).Name
509 if sym := ctx.findSymbol(name); sym != nil {
510 if sym.Node != nil {
511 if fd, ok := sym.Node.Data.(ast.FuncDeclNode); ok {
512 isVariadic = fd.HasVarargs
513 // Extract parameter types
514 for _, param := range fd.Params {
515 // Handle both typed parameters (VarDeclNode) and untyped parameters (IdentNode)
516 if paramData, ok := param.Data.(ast.VarDeclNode); ok {
517 expectedParamTypes = append(expectedParamTypes, paramData.Type)
518 }
519 // For IdentNode (untyped parameters), we can't extract type info, so skip
520 }
521 }
522 }
523 if !isVariadic && sym.Type == symExtrn {
524 isVariadic = true
525 }
526 }
527 }
528
529 argVals := make([]ir.Value, len(d.Args))
530 argTypes := make([]ir.Type, len(d.Args))
531 for i := len(d.Args) - 1; i >= 0; i-- {
532 argVals[i], _ = ctx.codegenExpr(d.Args[i])
533
534 // For typed functions with known parameter types, use the expected type
535 var expectedArgType *ast.BxType
536 if i < len(expectedParamTypes) {
537 expectedArgType = expectedParamTypes[i]
538 }
539
540 // If we have an expected type and the argument is a literal that can be coerced
541 if expectedArgType != nil && d.Args[i].Typ != nil {
542 argType := d.Args[i].Typ
543
544 // Handle float literal coercion to specific float types
545 if argType.Kind == ast.TYPE_LITERAL_FLOAT && expectedArgType.Kind == ast.TYPE_FLOAT {
546 // Debug warning for type coercion
547 if ctx.cfg.IsWarningEnabled(config.WarnDebugComp) {
548 util.Warn(ctx.cfg, config.WarnDebugComp, d.Args[i].Tok,
549 "Coercing float literal to %s for parameter %d", expectedArgType.Name, i+1)
550 }
551
552 expectedIrType := ir.GetType(expectedArgType, ctx.wordSize)
553 currentIrType := ir.GetType(argType, ctx.wordSize)
554
555 // Convert if types don't match
556 if currentIrType != expectedIrType {
557 convertedVal := ctx.newTemp()
558 ctx.addInstr(&ir.Instruction{
559 Op: ir.OpFToF,
560 Typ: expectedIrType,
561 Result: convertedVal,
562 Args: []ir.Value{argVals[i]},
563 })
564 argVals[i] = convertedVal
565 }
566 argTypes[i] = expectedIrType
567 } else {
568 argTypes[i] = ir.GetType(d.Args[i].Typ, ctx.wordSize)
569 }
570 } else {
571 argTypes[i] = ir.GetType(d.Args[i].Typ, ctx.wordSize)
572 }
573
574 if isVariadic && argTypes[i] == ir.TypeS {
575 promotedVal := ctx.newTemp()
576 ctx.addInstr(&ir.Instruction{
577 Op: ir.OpFToF,
578 Typ: ir.TypeD,
579 Result: promotedVal,
580 Args: []ir.Value{argVals[i]},
581 })
582 argVals[i] = promotedVal
583 argTypes[i] = ir.TypeD
584 }
585 }
586
587 isStmt := node.Parent != nil && node.Parent.Type == ast.Block
588 var res ir.Value
589 returnType := ir.GetType(node.Typ, ctx.wordSize)
590 callArgs := append([]ir.Value{funcVal}, argVals...)
591
592 if !isStmt && returnType != ir.TypeNone {
593 res = ctx.newTemp()
594 }
595
596 ctx.addInstr(&ir.Instruction{
597 Op: ir.OpCall,
598 Typ: returnType,
599 Result: res,
600 Args: callArgs,
601 ArgTypes: argTypes,
602 })
603
604 return res, false
605}
606
607func (ctx *Context) codegenTypeCast(node *ast.Node) (ir.Value, bool) {
608 d := node.Data.(ast.TypeCastNode)
609 val, _ := ctx.codegenExpr(d.Expr)
610
611 sourceType := d.Expr.Typ
612 targetType := d.TargetType
613
614 if ir.GetType(sourceType, ctx.wordSize) == ir.GetType(targetType, ctx.wordSize) {
615 return val, false
616 }
617
618 res := ctx.newTemp()
619 targetIrType := ir.GetType(targetType, ctx.wordSize)
620
621 sourceIsInt := ctx.isIntegerType(sourceType)
622 sourceIsFloat := ctx.isFloatType(sourceType)
623 targetIsInt := ctx.isIntegerType(targetType)
624 targetIsFloat := ctx.isFloatType(targetType)
625
626 op := ir.OpCast
627 if sourceIsInt && targetIsFloat {
628 op = ir.OpSWToF
629 if ctx.getSizeof(sourceType) == 8 {
630 op = ir.OpSLToF
631 }
632 } else if sourceIsFloat && targetIsFloat {
633 op = ir.OpFToF
634 } else if sourceIsFloat && targetIsInt {
635 op = ir.OpFToSI
636 } else if sourceIsInt && targetIsInt {
637 sourceSize, targetSize := ctx.getSizeof(sourceType), ctx.getSizeof(targetType)
638 if targetSize > sourceSize {
639 switch sourceSize {
640 case 1:
641 op = ir.OpExtSB
642 case 2:
643 op = ir.OpExtSH
644 case 4:
645 op = ir.OpExtSW
646 }
647 }
648 }
649
650 ctx.addInstr(&ir.Instruction{
651 Op: op,
652 Typ: targetIrType,
653 Result: res,
654 Args: []ir.Value{val},
655 })
656
657 return res, false
658}
659
660func (ctx *Context) codegenTernary(node *ast.Node) (ir.Value, bool) {
661 d := node.Data.(ast.TernaryNode)
662 thenL, elseL, endL := ctx.newLabel(), ctx.newLabel(), ctx.newLabel()
663 res := ctx.newTemp()
664 resType := ir.GetType(node.Typ, ctx.wordSize)
665
666 ctx.codegenLogicalCond(d.Cond, thenL, elseL)
667
668 ctx.startBlock(thenL)
669 thenVal, thenTerminates := ctx.codegenExpr(d.ThenExpr)
670 var thenPred *ir.Label
671 if !thenTerminates {
672 if ir.GetType(d.ThenExpr.Typ, ctx.wordSize) != resType {
673 castedVal := ctx.newTemp()
674 ctx.addInstr(&ir.Instruction{Op: ir.OpCast, Typ: resType, Result: castedVal, Args: []ir.Value{thenVal}})
675 thenVal = castedVal
676 }
677 thenPred = ctx.currentBlock.Label
678 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{endL}})
679 }
680
681 ctx.startBlock(elseL)
682 elseVal, elseTerminates := ctx.codegenExpr(d.ElseExpr)
683 var elsePred *ir.Label
684 if !elseTerminates {
685 if ir.GetType(d.ElseExpr.Typ, ctx.wordSize) != resType {
686 castedVal := ctx.newTemp()
687 ctx.addInstr(&ir.Instruction{Op: ir.OpCast, Typ: resType, Result: castedVal, Args: []ir.Value{elseVal}})
688 elseVal = castedVal
689 }
690 elsePred = ctx.currentBlock.Label
691 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{endL}})
692 }
693
694 terminates := thenTerminates && elseTerminates
695 if !terminates {
696 ctx.startBlock(endL)
697 phiArgs := []ir.Value{}
698 if !thenTerminates {
699 phiArgs = append(phiArgs, thenPred, thenVal)
700 }
701 if !elseTerminates {
702 phiArgs = append(phiArgs, elsePred, elseVal)
703 }
704 ctx.addInstr(&ir.Instruction{Op: ir.OpPhi, Typ: resType, Result: res, Args: phiArgs})
705 }
706 return res, terminates
707}
708
709func (ctx *Context) codegenAutoAlloc(node *ast.Node) (ir.Value, bool) {
710 d := node.Data.(ast.AutoAllocNode)
711 sizeVal, _ := ctx.codegenExpr(d.Size)
712 res := ctx.newTemp()
713
714 sizeInBytes := ctx.newTemp()
715 wordType := ir.GetType(nil, ctx.wordSize)
716 ctx.addInstr(&ir.Instruction{
717 Op: ir.OpMul,
718 Typ: wordType,
719 Result: sizeInBytes,
720 Args: []ir.Value{sizeVal, &ir.Const{Value: int64(ctx.wordSize)}},
721 })
722
723 ctx.addInstr(&ir.Instruction{
724 Op: ir.OpAlloc,
725 Typ: wordType,
726 Result: res,
727 Args: []ir.Value{sizeInBytes},
728 Align: ctx.stackAlign,
729 })
730 return res, false
731}
732
733func (ctx *Context) codegenStructLiteral(node *ast.Node) (ir.Value, bool) {
734 d := node.Data.(ast.StructLiteralNode)
735 structType := node.Typ
736 if structType == nil || structType.Kind != ast.TYPE_STRUCT {
737 util.Error(node.Tok, "internal: struct literal has invalid type")
738 return nil, false
739 }
740
741 size := ctx.getSizeof(structType)
742 align := ctx.getAlignof(structType)
743 structPtr := ctx.newTemp()
744 ctx.addInstr(&ir.Instruction{
745 Op: ir.OpAlloc,
746 Typ: ir.GetType(nil, ctx.wordSize),
747 Result: structPtr,
748 Args: []ir.Value{&ir.Const{Value: size}},
749 Align: int(align),
750 })
751
752 if d.Names == nil {
753 var currentOffset int64
754 for i, valNode := range d.Values {
755 field := structType.Fields[i].Data.(ast.VarDeclNode)
756 fieldAlign := ctx.getAlignof(field.Type)
757 currentOffset = util.AlignUp(currentOffset, fieldAlign)
758 fieldAddr := ctx.newTemp()
759 ctx.addInstr(&ir.Instruction{
760 Op: ir.OpAdd,
761 Typ: ir.GetType(nil, ctx.wordSize),
762 Result: fieldAddr,
763 Args: []ir.Value{structPtr, &ir.Const{Value: currentOffset}},
764 })
765 val, _ := ctx.codegenExpr(valNode)
766 ctx.genStore(fieldAddr, val, field.Type)
767 currentOffset += ctx.getSizeof(field.Type)
768 }
769 } else {
770 fieldOffsets := make(map[string]int64)
771 fieldTypes := make(map[string]*ast.BxType)
772 var currentOffset int64
773 for _, fieldNode := range structType.Fields {
774 fieldData := fieldNode.Data.(ast.VarDeclNode)
775 fieldAlign := ctx.getAlignof(fieldData.Type)
776 currentOffset = util.AlignUp(currentOffset, fieldAlign)
777 fieldOffsets[fieldData.Name] = currentOffset
778 fieldTypes[fieldData.Name] = fieldData.Type
779 currentOffset += ctx.getSizeof(fieldData.Type)
780 }
781
782 for i, nameNode := range d.Names {
783 fieldName := nameNode.Data.(ast.IdentNode).Name
784 offset, ok := fieldOffsets[fieldName]
785 if !ok {
786 util.Error(nameNode.Tok, "internal: struct '%s' has no field '%s'", structType.Name, fieldName)
787 continue
788 }
789 fieldAddr := ctx.newTemp()
790 ctx.addInstr(&ir.Instruction{
791 Op: ir.OpAdd,
792 Typ: ir.GetType(nil, ctx.wordSize),
793 Result: fieldAddr,
794 Args: []ir.Value{structPtr, &ir.Const{Value: offset}},
795 })
796
797 val, _ := ctx.codegenExpr(d.Values[i])
798 ctx.genStore(fieldAddr, val, fieldTypes[fieldName])
799 }
800 }
801
802 return structPtr, false
803}
804
805func (ctx *Context) codegenArrayLiteral(node *ast.Node) (ir.Value, bool) {
806 d := node.Data.(ast.ArrayLiteralNode)
807
808 // For array literals, we need the element type and count from the literal itself
809 elemType := d.ElementType
810 elemSize := ctx.getSizeof(elemType)
811 elemAlign := ctx.getAlignof(elemType)
812 arraySize := int64(len(d.Values)) * elemSize
813
814 // Allocate memory for the array
815 arrayPtr := ctx.newTemp()
816 ctx.addInstr(&ir.Instruction{
817 Op: ir.OpAlloc,
818 Typ: ir.GetType(nil, ctx.wordSize),
819 Result: arrayPtr,
820 Args: []ir.Value{&ir.Const{Value: arraySize}},
821 Align: int(elemAlign),
822 })
823
824 // Initialize each element
825 for i, valNode := range d.Values {
826 elemOffset := int64(i) * elemSize
827 elemAddr := ctx.newTemp()
828 ctx.addInstr(&ir.Instruction{
829 Op: ir.OpAdd,
830 Typ: ir.GetType(nil, ctx.wordSize),
831 Result: elemAddr,
832 Args: []ir.Value{arrayPtr, &ir.Const{Value: elemOffset}},
833 })
834 val, _ := ctx.codegenExpr(valNode)
835 ctx.genStore(elemAddr, val, elemType)
836 }
837
838 return arrayPtr, false
839}
840
841func (ctx *Context) codegenReturn(node *ast.Node) bool {
842 d := node.Data.(ast.ReturnNode)
843 var retVal ir.Value
844 if d.Expr != nil {
845 retVal, _ = ctx.codegenExpr(d.Expr)
846 } else if ctx.currentFunc != nil && ctx.currentFunc.ReturnType != ir.TypeNone {
847 retVal = &ir.Const{Value: 0}
848 }
849 ctx.addInstr(&ir.Instruction{Op: ir.OpRet, Args: []ir.Value{retVal}})
850 ctx.currentBlock = nil
851 return true
852}
853
854func (ctx *Context) codegenIf(node *ast.Node) bool {
855 d := node.Data.(ast.IfNode)
856 thenL, endL := ctx.newLabel(), ctx.newLabel()
857 elseL := endL
858 if d.ElseBody != nil {
859 elseL = ctx.newLabel()
860 }
861
862 ctx.codegenLogicalCond(d.Cond, thenL, elseL)
863
864 ctx.startBlock(thenL)
865 thenTerminates := ctx.codegenStmt(d.ThenBody)
866 if !thenTerminates {
867 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{endL}})
868 }
869
870 var elseTerminates bool
871 if d.ElseBody != nil {
872 ctx.startBlock(elseL)
873 elseTerminates = ctx.codegenStmt(d.ElseBody)
874 if !elseTerminates {
875 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{endL}})
876 }
877 }
878
879 if !thenTerminates || !elseTerminates {
880 ctx.startBlock(endL)
881 }
882 return thenTerminates && (d.ElseBody != nil && elseTerminates)
883}
884
885func (ctx *Context) codegenWhile(node *ast.Node) bool {
886 d := node.Data.(ast.WhileNode)
887 startL, bodyL, endL := ctx.newLabel(), ctx.newLabel(), ctx.newLabel()
888
889 oldBreak, oldContinue := ctx.breakLabel, ctx.continueLabel
890 ctx.breakLabel, ctx.continueLabel = endL, startL
891 defer func() { ctx.breakLabel, ctx.continueLabel = oldBreak, oldContinue }()
892
893 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{startL}})
894 ctx.startBlock(startL)
895 ctx.codegenLogicalCond(d.Cond, bodyL, endL)
896
897 ctx.startBlock(bodyL)
898 bodyTerminates := ctx.codegenStmt(d.Body)
899 if !bodyTerminates {
900 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{startL}})
901 }
902
903 ctx.startBlock(endL)
904 return false
905}
906
907func getBinaryOpAndType(op token.Type, resultAstType *ast.BxType, wordSize int) (ir.Op, ir.Type) {
908 if resultAstType != nil && (resultAstType.Kind == ast.TYPE_FLOAT || resultAstType.Kind == ast.TYPE_LITERAL_FLOAT) {
909 typ := ir.GetType(resultAstType, wordSize)
910 switch op {
911 case token.Plus, token.PlusEq, token.EqPlus: return ir.OpAddF, typ
912 case token.Minus, token.MinusEq, token.EqMinus: return ir.OpSubF, typ
913 case token.Star, token.StarEq, token.EqStar: return ir.OpMulF, typ
914 case token.Slash, token.SlashEq, token.EqSlash: return ir.OpDivF, typ
915 case token.Rem, token.RemEq, token.EqRem: return ir.OpRemF, typ
916 case token.EqEq: return ir.OpCEq, typ
917 case token.Neq: return ir.OpCNeq, typ
918 case token.Lt: return ir.OpCLt, typ
919 case token.Gt: return ir.OpCGt, typ
920 case token.Lte: return ir.OpCLe, typ
921 case token.Gte: return ir.OpCGe, typ
922 }
923 }
924
925 typ := ir.GetType(resultAstType, wordSize)
926 switch op {
927 case token.Plus, token.PlusEq, token.EqPlus: return ir.OpAdd, typ
928 case token.Minus, token.MinusEq, token.EqMinus: return ir.OpSub, typ
929 case token.Star, token.StarEq, token.EqStar: return ir.OpMul, typ
930 case token.Slash, token.SlashEq, token.EqSlash: return ir.OpDiv, typ
931 case token.Rem, token.RemEq, token.EqRem: return ir.OpRem, typ
932 case token.And, token.AndEq, token.EqAnd: return ir.OpAnd, typ
933 case token.Or, token.OrEq, token.EqOr: return ir.OpOr, typ
934 case token.Xor, token.XorEq, token.EqXor: return ir.OpXor, typ
935 case token.Shl, token.ShlEq, token.EqShl: return ir.OpShl, typ
936 case token.Shr, token.ShrEq, token.EqShr: return ir.OpShr, typ
937 case token.EqEq: return ir.OpCEq, typ
938 case token.Neq:
939 return ir.OpCNeq, typ
940 case token.Lt:
941 return ir.OpCLt, typ
942 case token.Gt:
943 return ir.OpCGt, typ
944 case token.Lte:
945 return ir.OpCLe, typ
946 case token.Gte:
947 return ir.OpCGe, typ
948 }
949 return -1, -1
950}
951
952// codegenTypeOf generates code for typeof(expr) which returns a string representation of the type
953func (ctx *Context) codegenTypeOf(node *ast.Node) (ir.Value, bool) {
954 d := node.Data.(ast.TypeOfNode)
955
956 // Type check the expression to determine its type
957 _, _ = ctx.codegenExpr(d.Expr)
958
959 // Get the type of the expression
960 var exprType *ast.BxType
961 if d.Expr.Typ != nil {
962 exprType = d.Expr.Typ
963 } else {
964 exprType = ast.TypeUntyped
965 }
966
967 // Convert the type to its string representation
968 typeStr := ast.TypeToString(exprType)
969
970 // Add the string to the string table and return a reference to it
971 return ctx.addString(typeStr), false
972}