xplshn
·
2025-09-10
codegen.go
Go
1package codegen
2
3import (
4 "fmt"
5
6 "github.com/xplshn/gbc/pkg/ast"
7 "github.com/xplshn/gbc/pkg/config"
8 "github.com/xplshn/gbc/pkg/ir"
9 "github.com/xplshn/gbc/pkg/token"
10 "github.com/xplshn/gbc/pkg/util"
11)
12
13type symbolType int
14
15const (
16 symVar symbolType = iota
17 symFunc
18 symLabel
19 symType
20 symExtrn
21)
22
23type symbol struct {
24 Name string
25 Type symbolType
26 BxType *ast.BxType
27 IRVal ir.Value
28 IsVector bool
29 IsByteArray bool
30 StackOffset int64
31 Next *symbol
32 Node *ast.Node
33}
34
35type scope struct{ Symbols *symbol; Parent *scope }
36
37type autoVarInfo struct{ Node *ast.Node; Size int64 }
38
39type Context struct {
40 prog *ir.Program
41 inlineAsm string
42 tempCount int
43 labelCount int
44 currentScope *scope
45 currentFunc *ir.Func
46 currentBlock *ir.BasicBlock
47 breakLabel *ir.Label
48 continueLabel *ir.Label
49 wordSize int
50 stackAlign int
51 isTypedPass bool
52 cfg *config.Config
53 switchCaseLabels map[*ast.Node]*ir.Label
54}
55
56func NewContext(cfg *config.Config) *Context {
57 return &Context{
58 prog: &ir.Program{
59 Strings: make(map[string]string),
60 ExtrnFuncs: make([]string, 0),
61 ExtrnVars: make(map[string]bool),
62 WordSize: cfg.WordSize,
63 GlobalSymbols: make(map[string]*ast.Node),
64 },
65 currentScope: newScope(nil),
66 wordSize: cfg.WordSize,
67 stackAlign: cfg.StackAlignment,
68 isTypedPass: cfg.IsFeatureEnabled(config.FeatTyped),
69 cfg: cfg,
70 switchCaseLabels: make(map[*ast.Node]*ir.Label),
71 }
72}
73
74func newScope(parent *scope) *scope { return &scope{Parent: parent} }
75
76func (ctx *Context) enterScope() { ctx.currentScope = newScope(ctx.currentScope) }
77func (ctx *Context) exitScope() {
78 if ctx.currentScope.Parent != nil { ctx.currentScope = ctx.currentScope.Parent }
79}
80
81func (ctx *Context) findSymbol(name string) *symbol {
82 return ctx.findSymbolOfType(name, -1, false) // -1 means any type except symType
83}
84
85func (ctx *Context) findTypeSymbol(name string) *symbol {
86 return ctx.findSymbolOfType(name, symType, false)
87}
88
89func (ctx *Context) findSymbolInCurrentScope(name string) *symbol {
90 return ctx.findSymbolOfType(name, -1, true) // any type, current scope only
91}
92
93func (ctx *Context) findSymbolOfType(name string, wantType symbolType, currentOnly bool) *symbol {
94 for s := ctx.currentScope; s != nil; s = s.Parent {
95 for sym := s.Symbols; sym != nil; sym = sym.Next {
96 if sym.Name == name {
97 if wantType == -1 || (wantType == symType) == (sym.Type == symType) { return sym }
98 }
99 }
100 if currentOnly { break }
101 }
102 return nil
103}
104
105func (ctx *Context) addSymbol(name string, symType symbolType, bxType *ast.BxType, isVector bool, node *ast.Node) *symbol {
106 var irVal ir.Value
107 switch symType {
108 case symVar:
109 if ctx.currentScope.Parent == nil {
110 irVal = &ir.Global{Name: name}
111 } else {
112 irVal = ctx.newTemp()
113 if t, ok := irVal.(*ir.Temporary); ok {
114 t.Name = name
115 }
116 }
117 case symFunc, symExtrn: irVal = &ir.Global{Name: name}
118 case symLabel: irVal = &ir.Label{Name: name}
119 }
120
121 sym := &symbol{
122 Name: name, Type: symType, BxType: bxType, IRVal: irVal,
123 IsVector: isVector, Next: ctx.currentScope.Symbols, Node: node,
124 }
125 ctx.currentScope.Symbols = sym
126 return sym
127}
128
129func (ctx *Context) newTemp() *ir.Temporary {
130 t := &ir.Temporary{ID: ctx.tempCount}
131 ctx.tempCount++
132 return t
133}
134
135func (ctx *Context) newLabel() *ir.Label {
136 l := &ir.Label{Name: fmt.Sprintf("L%d", ctx.labelCount)}
137 ctx.labelCount++
138 return l
139}
140
141func (ctx *Context) startBlock(label *ir.Label) {
142 block := &ir.BasicBlock{Label: label}
143 ctx.currentFunc.Blocks = append(ctx.currentFunc.Blocks, block)
144 ctx.currentBlock = block
145}
146
147func (ctx *Context) addInstr(instr *ir.Instruction) {
148 if ctx.currentBlock == nil { ctx.startBlock(ctx.newLabel()) }
149 ctx.currentBlock.Instructions = append(ctx.currentBlock.Instructions, instr)
150}
151
152func (ctx *Context) addString(value string) ir.Value {
153 if label, ok := ctx.prog.Strings[value]; ok { return &ir.Global{Name: label} }
154 label := fmt.Sprintf("str%d", len(ctx.prog.Strings))
155 ctx.prog.Strings[value] = label
156 return &ir.Global{Name: label}
157}
158
159func (ctx *Context) evalConstExpr(node *ast.Node) (int64, bool) {
160 if node == nil { return 0, false }
161 folded := ast.FoldConstants(node)
162 if folded.Type == ast.Number { return folded.Data.(ast.NumberNode).Value, true }
163 if folded.Type == ast.Ident {
164 identName := folded.Data.(ast.IdentNode).Name
165 sym := ctx.findSymbol(identName)
166 if sym != nil && sym.Node != nil && sym.Node.Type == ast.VarDecl {
167 decl := sym.Node.Data.(ast.VarDeclNode)
168 if len(decl.InitList) == 1 {
169 if decl.InitList[0] == node { return 0, false }
170 return ctx.evalConstExpr(decl.InitList[0])
171 }
172 }
173 }
174 return 0, false
175}
176
177func (ctx *Context) getSizeof(typ *ast.BxType) int64 {
178 if typ == nil || typ.Kind == ast.TYPE_UNTYPED { return int64(ctx.wordSize) }
179
180 switch typ.Kind {
181 case ast.TYPE_VOID: return 0
182 case ast.TYPE_POINTER: return int64(ctx.wordSize)
183 case ast.TYPE_ARRAY:
184 elemSize := ctx.getSizeof(typ.Base)
185 var arrayLen int64 = 1
186 if typ.ArraySize != nil {
187 if val, ok := ctx.evalConstExpr(typ.ArraySize); ok {
188 arrayLen = val
189 } else {
190 util.Error(typ.ArraySize.Tok, "Array size must be a constant expression")
191 }
192 }
193 return elemSize * arrayLen
194 case ast.TYPE_PRIMITIVE, ast.TYPE_LITERAL_INT:
195 resolver := ir.NewTypeSizeResolver(ctx.wordSize)
196 if size := resolver.GetTypeSize(typ.Name); size > 0 {
197 return size
198 }
199 // Fallback for user-defined types
200 if sym := ctx.findTypeSymbol(typ.Name); sym != nil {
201 return ctx.getSizeof(sym.BxType)
202 }
203 return int64(ctx.wordSize)
204 case ast.TYPE_ENUM:
205 return ctx.getSizeof(ast.TypeInt)
206 case ast.TYPE_FLOAT, ast.TYPE_LITERAL_FLOAT:
207 if typ.Kind == ast.TYPE_LITERAL_FLOAT {
208 return int64(ctx.wordSize)
209 }
210 resolver := ir.NewTypeSizeResolver(ctx.wordSize)
211 return resolver.GetTypeSize(typ.Name)
212 case ast.TYPE_STRUCT:
213 var totalSize, maxAlign int64 = 0, 1
214 for _, field := range typ.Fields {
215 fieldData := field.Data.(ast.VarDeclNode)
216 fieldAlign := ctx.getAlignof(fieldData.Type)
217 if fieldAlign > maxAlign {
218 maxAlign = fieldAlign
219 }
220 totalSize = util.AlignUp(totalSize, fieldAlign)
221 totalSize += ctx.getSizeof(fieldData.Type)
222 }
223 if maxAlign == 0 {
224 maxAlign = 1
225 }
226 return util.AlignUp(totalSize, maxAlign)
227 }
228 return int64(ctx.wordSize)
229}
230
231func (ctx *Context) getAlignof(typ *ast.BxType) int64 {
232 if typ == nil { return int64(ctx.wordSize) }
233
234 if (typ.Kind == ast.TYPE_PRIMITIVE || typ.Kind == ast.TYPE_STRUCT) && typ.Name != "" {
235 if sym := ctx.findTypeSymbol(typ.Name); sym != nil {
236 if sym.BxType != typ { return ctx.getAlignof(sym.BxType) }
237 }
238 }
239
240 if typ.Kind == ast.TYPE_UNTYPED { return int64(ctx.wordSize) }
241 switch typ.Kind {
242 case ast.TYPE_VOID: return 1
243 case ast.TYPE_POINTER: return int64(ctx.wordSize)
244 case ast.TYPE_ARRAY: return ctx.getAlignof(typ.Base)
245 case ast.TYPE_PRIMITIVE, ast.TYPE_FLOAT, ast.TYPE_ENUM, ast.TYPE_LITERAL_INT, ast.TYPE_LITERAL_FLOAT: return ctx.getSizeof(typ)
246 case ast.TYPE_STRUCT:
247 var maxAlign int64 = 1
248 for _, field := range typ.Fields {
249 fieldAlign := ctx.getAlignof(field.Data.(ast.VarDeclNode).Type)
250 if fieldAlign > maxAlign { maxAlign = fieldAlign }
251 }
252 return maxAlign
253 }
254 return int64(ctx.wordSize)
255}
256
257func (ctx *Context) GenerateIR(root *ast.Node) (*ir.Program, string) {
258 ctx.collectGlobals(root)
259 ctx.collectStrings(root)
260 if !ctx.isTypedPass {
261 ctx.findByteArrays(root)
262 }
263 ctx.codegenStmt(root)
264
265 ctx.prog.BackendTempCount = ctx.tempCount
266 return ctx.prog, ctx.inlineAsm
267}
268
269func walkAST(node *ast.Node, visitor func(n *ast.Node)) {
270 if node == nil { return }
271 visitor(node)
272
273 switch d := node.Data.(type) {
274 case ast.AssignNode:
275 walkAST(d.Lhs, visitor)
276 walkAST(d.Rhs, visitor)
277 case ast.BinaryOpNode:
278 walkAST(d.Left, visitor)
279 walkAST(d.Right, visitor)
280 case ast.UnaryOpNode:
281 walkAST(d.Expr, visitor)
282 case ast.PostfixOpNode:
283 walkAST(d.Expr, visitor)
284 case ast.IndirectionNode:
285 walkAST(d.Expr, visitor)
286 case ast.AddressOfNode:
287 walkAST(d.LValue, visitor)
288 case ast.TernaryNode:
289 walkAST(d.Cond, visitor)
290 walkAST(d.ThenExpr, visitor)
291 walkAST(d.ElseExpr, visitor)
292 case ast.SubscriptNode:
293 walkAST(d.Array, visitor)
294 walkAST(d.Index, visitor)
295 case ast.FuncCallNode:
296 walkAST(d.FuncExpr, visitor)
297 for _, arg := range d.Args {
298 walkAST(arg, visitor)
299 }
300 case ast.FuncDeclNode:
301 walkAST(d.Body, visitor)
302 case ast.VarDeclNode:
303 for _, init := range d.InitList {
304 walkAST(init, visitor)
305 }
306 walkAST(d.SizeExpr, visitor)
307 case ast.MultiVarDeclNode:
308 for _, decl := range d.Decls {
309 walkAST(decl, visitor)
310 }
311 case ast.IfNode:
312 walkAST(d.Cond, visitor)
313 walkAST(d.ThenBody, visitor)
314 walkAST(d.ElseBody, visitor)
315 case ast.WhileNode:
316 walkAST(d.Cond, visitor)
317 walkAST(d.Body, visitor)
318 case ast.ReturnNode:
319 walkAST(d.Expr, visitor)
320 case ast.BlockNode:
321 for _, s := range d.Stmts {
322 walkAST(s, visitor)
323 }
324 case ast.SwitchNode:
325 walkAST(d.Expr, visitor)
326 walkAST(d.Body, visitor)
327 case ast.CaseNode:
328 for _, v := range d.Values {
329 walkAST(v, visitor)
330 }
331 walkAST(d.Body, visitor)
332 case ast.DefaultNode:
333 walkAST(d.Body, visitor)
334 case ast.LabelNode:
335 walkAST(d.Stmt, visitor)
336 }
337}
338
339func (ctx *Context) collectGlobals(node *ast.Node) {
340 if node == nil {
341 return
342 }
343
344 switch node.Type {
345 case ast.Block:
346 for _, stmt := range node.Data.(ast.BlockNode).Stmts {
347 ctx.collectGlobals(stmt)
348 }
349 case ast.VarDecl:
350 if ctx.currentScope.Parent == nil {
351 d := node.Data.(ast.VarDeclNode)
352 existingSym := ctx.findSymbolInCurrentScope(d.Name)
353 if existingSym == nil {
354 ctx.addSymbol(d.Name, symVar, d.Type, d.IsVector, node)
355 } else if existingSym.Type == symFunc || existingSym.Type == symExtrn {
356 util.Warn(ctx.cfg, config.WarnExtra, node.Tok, "Definition of '%s' overrides previous external declaration", d.Name)
357 existingSym.Type, existingSym.IsVector, existingSym.BxType, existingSym.Node = symVar, d.IsVector, d.Type, node
358 } else if existingSym.Type == symVar {
359 util.Warn(ctx.cfg, config.WarnExtra, node.Tok, "Redefinition of variable '%s'", d.Name)
360 existingSym.IsVector, existingSym.BxType, existingSym.Node = d.IsVector, d.Type, node
361 }
362 }
363 case ast.MultiVarDecl:
364 if ctx.currentScope.Parent == nil {
365 for _, decl := range node.Data.(ast.MultiVarDeclNode).Decls {
366 ctx.collectGlobals(decl)
367 }
368 }
369 case ast.FuncDecl:
370 d := node.Data.(ast.FuncDeclNode)
371 ctx.prog.GlobalSymbols[d.Name] = node
372 existingSym := ctx.findSymbolInCurrentScope(d.Name)
373 if existingSym == nil {
374 ctx.addSymbol(d.Name, symFunc, d.ReturnType, false, node)
375 } else if existingSym.Type != symFunc {
376 util.Warn(ctx.cfg, config.WarnExtra, node.Tok, "Redefinition of '%s' as a function", d.Name)
377 existingSym.Type, existingSym.IsVector, existingSym.BxType, existingSym.Node = symFunc, false, d.ReturnType, node
378 }
379 case ast.ExtrnDecl:
380 d := node.Data.(ast.ExtrnDeclNode)
381 for _, nameNode := range d.Names {
382 name := nameNode.Data.(ast.IdentNode).Name
383 if ctx.findSymbolInCurrentScope(name) == nil {
384 ctx.addSymbol(name, symExtrn, ast.TypeUntyped, false, nameNode)
385 isAlreadyExtrn := false
386 for _, extrnName := range ctx.prog.ExtrnFuncs {
387 if extrnName == name {
388 isAlreadyExtrn = true
389 break
390 }
391 }
392 if !isAlreadyExtrn {
393 ctx.prog.ExtrnFuncs = append(ctx.prog.ExtrnFuncs, name)
394 }
395 }
396 }
397 case ast.TypeDecl:
398 d := node.Data.(ast.TypeDeclNode)
399 if ctx.findSymbolInCurrentScope(d.Name) == nil {
400 ctx.addSymbol(d.Name, symType, d.Type, false, node)
401 }
402 case ast.EnumDecl:
403 d := node.Data.(ast.EnumDeclNode)
404 if ctx.findSymbolInCurrentScope(d.Name) == nil {
405 enumType := &ast.BxType{Kind: ast.TYPE_ENUM, Name: d.Name, EnumMembers: d.Members, Base: ast.TypeInt}
406 ctx.addSymbol(d.Name, symType, enumType, false, node)
407 }
408 for _, memberNode := range d.Members {
409 ctx.collectGlobals(memberNode)
410 }
411 }
412}
413
414func (ctx *Context) findByteArrays(root *ast.Node) {
415 for {
416 changedInPass := false
417 visitor := func(n *ast.Node) {
418 if n == nil {
419 return
420 }
421 switch n.Type {
422 case ast.VarDecl:
423 d := n.Data.(ast.VarDeclNode)
424 if d.IsVector && len(d.InitList) == 1 && d.InitList[0].Type == ast.String {
425 if sym := ctx.findSymbol(d.Name); sym != nil && !sym.IsByteArray {
426 sym.IsByteArray = true
427 changedInPass = true
428 }
429 }
430 case ast.Assign:
431 d := n.Data.(ast.AssignNode)
432 if d.Lhs.Type != ast.Ident {
433 return
434 }
435 lhsSym := ctx.findSymbol(d.Lhs.Data.(ast.IdentNode).Name)
436 if lhsSym == nil || lhsSym.IsByteArray {
437 return
438 }
439 rhsIsByteArray := false
440 switch d.Rhs.Type {
441 case ast.String:
442 rhsIsByteArray = true
443 case ast.Ident:
444 if rhsSym := ctx.findSymbol(d.Rhs.Data.(ast.IdentNode).Name); rhsSym != nil && rhsSym.IsByteArray {
445 rhsIsByteArray = true
446 }
447 }
448 if rhsIsByteArray {
449 lhsSym.IsByteArray = true
450 changedInPass = true
451 }
452 }
453 }
454 walkAST(root, visitor)
455 if !changedInPass {
456 break
457 }
458 }
459}
460
461func (ctx *Context) collectStrings(root *ast.Node) {
462 walkAST(root, func(n *ast.Node) {
463 if n != nil && n.Type == ast.String {
464 ctx.addString(n.Data.(ast.StringNode).Value)
465 }
466 })
467}
468
469func (ctx *Context) genLoad(addr ir.Value, typ *ast.BxType) ir.Value {
470 res := ctx.newTemp()
471 loadType := ir.GetType(typ, ctx.wordSize)
472 ctx.addInstr(&ir.Instruction{Op: ir.OpLoad, Typ: loadType, Result: res, Args: []ir.Value{addr}})
473 return res
474}
475
476func (ctx *Context) genStore(addr, value ir.Value, typ *ast.BxType) {
477 storeType := ir.GetType(typ, ctx.wordSize)
478 ctx.addInstr(&ir.Instruction{Op: ir.OpStore, Typ: storeType, Args: []ir.Value{value, addr}})
479}
480
481func (ctx *Context) codegenMemberAccessAddr(node *ast.Node) ir.Value {
482 d := node.Data.(ast.MemberAccessNode)
483 structType := d.Expr.Typ
484
485 if structType == nil {
486 if d.Expr.Type == ast.Ident {
487 if sym := ctx.findSymbol(d.Expr.Data.(ast.IdentNode).Name); sym != nil {
488 structType = sym.BxType
489 }
490 }
491 }
492
493 if structType == nil {
494 util.Error(node.Tok, "internal: cannot determine type of struct for member access")
495 return nil
496 }
497
498 var structAddr ir.Value
499 if structType.Kind == ast.TYPE_POINTER {
500 structAddr, _ = ctx.codegenExpr(d.Expr)
501 } else {
502 // Check if this is a struct parameter (which is passed as pointer)
503 if d.Expr.Type == ast.Ident {
504 name := d.Expr.Data.(ast.IdentNode).Name
505 if sym := ctx.findSymbol(name); sym != nil {
506 // Check if this is a function parameter that has struct type
507 isStructParam := false
508 if sym.Node != nil && sym.Node.Parent != nil && sym.Node.Parent.Type == ast.FuncDecl {
509 // Resolve the struct type
510 paramStructType := structType
511 if paramStructType != nil && paramStructType.Kind != ast.TYPE_STRUCT && paramStructType.Name != "" {
512 if typeSym := ctx.findTypeSymbol(paramStructType.Name); typeSym != nil && typeSym.BxType.Kind == ast.TYPE_STRUCT {
513 paramStructType = typeSym.BxType
514 }
515 }
516 if paramStructType != nil && paramStructType.Kind == ast.TYPE_STRUCT {
517 isStructParam = true
518 }
519 }
520
521 if isStructParam {
522 // For struct parameters, use the parameter value directly (it's already a pointer)
523 structAddr, _ = ctx.codegenExpr(d.Expr)
524 } else {
525 structAddr = ctx.codegenLvalue(d.Expr)
526 }
527 } else {
528 structAddr = ctx.codegenLvalue(d.Expr)
529 }
530 } else {
531 structAddr = ctx.codegenLvalue(d.Expr)
532 }
533 }
534
535 baseType := structType
536 if baseType.Kind == ast.TYPE_POINTER {
537 baseType = baseType.Base
538 }
539
540 if baseType.Kind != ast.TYPE_STRUCT && baseType.Name != "" {
541 if sym := ctx.findTypeSymbol(baseType.Name); sym != nil && sym.BxType.Kind == ast.TYPE_STRUCT {
542 baseType = sym.BxType
543 }
544 }
545
546 if baseType.Kind != ast.TYPE_STRUCT {
547 util.Error(node.Tok, "internal: member access on non-struct type '%s'", baseType.Name)
548 return nil
549 }
550
551 var offset int64
552 found := false
553 memberName := d.Member.Data.(ast.IdentNode).Name
554
555 for _, fieldNode := range baseType.Fields {
556 fieldData := fieldNode.Data.(ast.VarDeclNode)
557 fieldAlign := ctx.getAlignof(fieldData.Type)
558
559 offset = util.AlignUp(offset, fieldAlign)
560
561 if fieldData.Name == memberName {
562 found = true
563 break
564 }
565 offset += ctx.getSizeof(fieldData.Type)
566 }
567
568 if !found {
569 util.Error(node.Tok, "internal: could not find member '%s' during codegen", memberName)
570 return nil
571 }
572
573 if offset == 0 {
574 return structAddr
575 }
576
577 resultAddr := ctx.newTemp()
578 ctx.addInstr(&ir.Instruction{
579 Op: ir.OpAdd,
580 Typ: ir.GetType(nil, ctx.wordSize),
581 Result: resultAddr,
582 Args: []ir.Value{structAddr, &ir.Const{Value: offset}},
583 })
584 return resultAddr
585}
586
587func (ctx *Context) codegenLvalue(node *ast.Node) ir.Value {
588 if node == nil {
589 util.Error(token.Token{}, "Internal error: null l-value node in codegen")
590 return nil
591 }
592 switch node.Type {
593 case ast.Ident:
594 name := node.Data.(ast.IdentNode).Name
595 sym := ctx.findSymbol(name)
596 if sym == nil {
597 util.Warn(ctx.cfg, config.WarnImplicitDecl, node.Tok, "Implicit declaration of variable '%s'", name)
598 sym = ctx.addSymbol(name, symVar, ast.TypeUntyped, false, node)
599 }
600 if sym.Type == symFunc {
601 util.Error(node.Tok, "Cannot assign to function '%s'", name)
602 return nil
603 }
604 return sym.IRVal
605 case ast.Indirection:
606 res, _ := ctx.codegenExpr(node.Data.(ast.IndirectionNode).Expr)
607 return res
608 case ast.Subscript:
609 return ctx.codegenSubscriptAddr(node)
610 case ast.MemberAccess:
611 return ctx.codegenMemberAccessAddr(node)
612 case ast.FuncCall:
613 if node.Typ != nil && node.Typ.Kind == ast.TYPE_STRUCT {
614 res, _ := ctx.codegenExpr(node)
615 return res
616 }
617 }
618 util.Error(node.Tok, "Expression is not a valid l-value")
619 return nil
620}
621
622func (ctx *Context) codegenLogicalCond(node *ast.Node, trueL, falseL *ir.Label) {
623 if node.Type == ast.BinaryOp {
624 d := node.Data.(ast.BinaryOpNode)
625 if d.Op == token.OrOr {
626 newFalseL := ctx.newLabel()
627 ctx.codegenLogicalCond(d.Left, trueL, newFalseL)
628 ctx.startBlock(newFalseL)
629 ctx.codegenLogicalCond(d.Right, trueL, falseL)
630 return
631 }
632 if d.Op == token.AndAnd {
633 newTrueL := ctx.newLabel()
634 ctx.codegenLogicalCond(d.Left, newTrueL, falseL)
635 ctx.startBlock(newTrueL)
636 ctx.codegenLogicalCond(d.Right, trueL, falseL)
637 return
638 }
639 }
640
641 condVal, _ := ctx.codegenExpr(node)
642 ctx.addInstr(&ir.Instruction{Op: ir.OpJnz, Args: []ir.Value{condVal, trueL, falseL}})
643 ctx.currentBlock = nil
644}
645
646func (ctx *Context) codegenExpr(node *ast.Node) (result ir.Value, terminates bool) {
647 if node == nil {
648 return &ir.Const{Value: 0}, false
649 }
650
651 switch node.Type {
652 case ast.Number:
653 return &ir.Const{Value: node.Data.(ast.NumberNode).Value}, false
654 case ast.FloatNumber:
655 typ := ir.GetType(node.Typ, ctx.wordSize)
656 return &ir.FloatConst{Value: node.Data.(ast.FloatNumberNode).Value, Typ: typ}, false
657 case ast.String:
658 return ctx.addString(node.Data.(ast.StringNode).Value), false
659 case ast.Nil:
660 return &ir.Const{Value: 0}, false
661 case ast.Ident:
662 return ctx.codegenIdent(node)
663 case ast.Assign:
664 return ctx.codegenAssign(node)
665 case ast.MultiAssign:
666 return ctx.codegenMultiAssign(node)
667 case ast.BinaryOp:
668 return ctx.codegenBinaryOp(node)
669 case ast.UnaryOp:
670 return ctx.codegenUnaryOp(node)
671 case ast.PostfixOp:
672 return ctx.codegenPostfixOp(node)
673 case ast.Indirection:
674 return ctx.codegenIndirection(node)
675 case ast.Subscript:
676 addr := ctx.codegenSubscriptAddr(node)
677 return ctx.genLoad(addr, node.Typ), false
678 case ast.AddressOf:
679 return ctx.codegenAddressOf(node)
680 case ast.FuncCall:
681 return ctx.codegenFuncCall(node)
682 case ast.TypeCast:
683 return ctx.codegenTypeCast(node)
684 case ast.TypeOf:
685 return ctx.codegenTypeOf(node)
686 case ast.Ternary:
687 return ctx.codegenTernary(node)
688 case ast.AutoAlloc:
689 return ctx.codegenAutoAlloc(node)
690 case ast.StructLiteral:
691 return ctx.codegenStructLiteral(node)
692 case ast.ArrayLiteral:
693 return ctx.codegenArrayLiteral(node)
694 case ast.MemberAccess:
695 addr := ctx.codegenMemberAccessAddr(node)
696 if addr == nil {
697 return nil, true
698 }
699 return ctx.genLoad(addr, node.Typ), false
700 }
701 util.Error(node.Tok, "Internal error: unhandled expression type in codegen: %v", node.Type)
702 return nil, true
703}
704
705func (ctx *Context) codegenStmt(node *ast.Node) (terminates bool) {
706 if node == nil {
707 return false
708 }
709 switch node.Type {
710 case ast.Block:
711 isRealBlock := !node.Data.(ast.BlockNode).IsSynthetic
712 if isRealBlock {
713 ctx.enterScope()
714 }
715 var blockTerminates bool
716 for _, stmt := range node.Data.(ast.BlockNode).Stmts {
717 if blockTerminates {
718 isLabel := stmt.Type == ast.Label || stmt.Type == ast.Case || stmt.Type == ast.Default
719 if !isLabel {
720 util.Warn(ctx.cfg, config.WarnUnreachableCode, stmt.Tok, "Unreachable code")
721 continue
722 }
723 blockTerminates = false
724 ctx.currentBlock = nil
725 }
726 blockTerminates = ctx.codegenStmt(stmt)
727 }
728 if isRealBlock {
729 ctx.exitScope()
730 }
731 return blockTerminates
732
733 case ast.FuncDecl:
734 ctx.codegenFuncDecl(node)
735 return false
736 case ast.VarDecl:
737 ctx.codegenVarDecl(node)
738 return false
739 case ast.MultiVarDecl:
740 for _, decl := range node.Data.(ast.MultiVarDeclNode).Decls {
741 ctx.codegenVarDecl(decl)
742 }
743 return false
744 case ast.TypeDecl, ast.Directive:
745 return false
746 case ast.EnumDecl:
747 // Process enum members as global variable declarations
748 d := node.Data.(ast.EnumDeclNode)
749 for _, memberNode := range d.Members {
750 ctx.codegenVarDecl(memberNode)
751 }
752 return false
753 case ast.ExtrnDecl:
754 d := node.Data.(ast.ExtrnDeclNode)
755 for _, nameNode := range d.Names {
756 name := nameNode.Data.(ast.IdentNode).Name
757 if ctx.findSymbol(name) == nil {
758 ctx.addSymbol(name, symExtrn, ast.TypeUntyped, false, nameNode)
759 }
760 }
761 return false
762 case ast.Return:
763 return ctx.codegenReturn(node)
764 case ast.If:
765 return ctx.codegenIf(node)
766 case ast.While:
767 return ctx.codegenWhile(node)
768 case ast.Switch:
769 return ctx.codegenSwitch(node)
770 case ast.Label:
771 d := node.Data.(ast.LabelNode)
772 label := &ir.Label{Name: d.Name}
773 if ctx.currentBlock != nil {
774 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{label}})
775 }
776 ctx.startBlock(label)
777 return ctx.codegenStmt(d.Stmt)
778
779 case ast.Goto:
780 d := node.Data.(ast.GotoNode)
781 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{&ir.Label{Name: d.Label}}})
782 ctx.currentBlock = nil
783 return true
784
785 case ast.Break:
786 if ctx.breakLabel == nil {
787 util.Error(node.Tok, "'break' not in a loop or switch")
788 }
789 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{ctx.breakLabel}})
790 ctx.currentBlock = nil
791 return true
792
793 case ast.Continue:
794 if ctx.continueLabel == nil {
795 util.Error(node.Tok, "'continue' not in a loop")
796 }
797 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{ctx.continueLabel}})
798 ctx.currentBlock = nil
799 return true
800
801 case ast.Case:
802 if label, ok := ctx.switchCaseLabels[node]; ok {
803 if ctx.currentBlock != nil {
804 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{label}})
805 }
806 ctx.startBlock(label)
807 return ctx.codegenStmt(node.Data.(ast.CaseNode).Body)
808 }
809 util.Error(node.Tok, "'case' statement not properly nested in a switch context")
810 return false
811
812 case ast.Default:
813 if label, ok := ctx.switchCaseLabels[node]; ok {
814 if ctx.currentBlock != nil {
815 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{label}})
816 }
817 ctx.startBlock(label)
818 return ctx.codegenStmt(node.Data.(ast.DefaultNode).Body)
819 }
820 util.Error(node.Tok, "'default' statement not properly nested in a switch context")
821 return false
822
823 default:
824 // Expression statements are not allowed at global scope
825 if ctx.currentFunc == nil {
826 util.Error(node.Tok, "Expression statements are not allowed at global scope")
827 return false
828 }
829 _, terminates := ctx.codegenExpr(node)
830 return terminates
831 }
832}
833
834func (ctx *Context) codegenSwitch(node *ast.Node) bool {
835 d := node.Data.(ast.SwitchNode)
836 switchVal, _ := ctx.codegenExpr(d.Expr)
837 endLabel := ctx.newLabel()
838 var defaultTarget *ir.Label
839
840 oldBreak := ctx.breakLabel
841 ctx.breakLabel = endLabel
842 defer func() { ctx.breakLabel = oldBreak }()
843
844 caseLabels := make(map[*ast.Node]*ir.Label)
845 var caseOrder []*ast.Node
846 var findCasesRecursive func(*ast.Node)
847 findCasesRecursive = func(n *ast.Node) {
848 if n == nil || (n.Type == ast.Switch && n != node) {
849 return
850 }
851 if n.Type == ast.Case || n.Type == ast.Default {
852 if _, exists := caseLabels[n]; !exists {
853 label := ctx.newLabel()
854 caseLabels[n] = label
855 caseOrder = append(caseOrder, n)
856 if n.Type == ast.Default {
857 if defaultTarget != nil {
858 util.Error(n.Tok, "multiple default labels in switch")
859 }
860 defaultTarget = label
861 }
862 }
863 }
864 switch data := n.Data.(type) {
865 case ast.BlockNode:
866 for _, stmt := range data.Stmts {
867 findCasesRecursive(stmt)
868 }
869 case ast.IfNode:
870 findCasesRecursive(data.ThenBody)
871 findCasesRecursive(data.ElseBody)
872 case ast.WhileNode:
873 findCasesRecursive(data.Body)
874 case ast.LabelNode:
875 findCasesRecursive(data.Stmt)
876 case ast.CaseNode:
877 findCasesRecursive(data.Body)
878 case ast.DefaultNode:
879 findCasesRecursive(data.Body)
880 }
881 }
882 findCasesRecursive(d.Body)
883
884 if defaultTarget == nil {
885 defaultTarget = endLabel
886 }
887
888 for _, caseStmt := range caseOrder {
889 if caseStmt.Type == ast.Case {
890 caseData := caseStmt.Data.(ast.CaseNode)
891 bodyLabel := caseLabels[caseStmt]
892 nextCaseCheck := ctx.newLabel()
893
894 var finalCond ir.Value
895 for i, valueExpr := range caseData.Values {
896 caseVal, _ := ctx.codegenExpr(valueExpr)
897 cmpRes := ctx.newTemp()
898 ctx.addInstr(&ir.Instruction{Op: ir.OpCEq, Typ: ir.GetType(nil, ctx.wordSize), OperandType: ir.GetType(d.Expr.Typ, ctx.wordSize), Result: cmpRes, Args: []ir.Value{switchVal, caseVal}})
899 if i == 0 {
900 finalCond = cmpRes
901 } else {
902 newFinalCond := ctx.newTemp()
903 ctx.addInstr(&ir.Instruction{Op: ir.OpOr, Typ: ir.GetType(nil, ctx.wordSize), Result: newFinalCond, Args: []ir.Value{finalCond, cmpRes}})
904 finalCond = newFinalCond
905 }
906 }
907
908 if finalCond != nil {
909 ctx.addInstr(&ir.Instruction{Op: ir.OpJnz, Args: []ir.Value{finalCond, bodyLabel, nextCaseCheck}})
910 } else {
911 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{nextCaseCheck}})
912 }
913 ctx.startBlock(nextCaseCheck)
914 }
915 }
916 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{defaultTarget}})
917 ctx.currentBlock = nil
918
919 oldCaseLabels := ctx.switchCaseLabels
920 ctx.switchCaseLabels = caseLabels
921 defer func() { ctx.switchCaseLabels = oldCaseLabels }()
922
923 terminates := ctx.codegenStmt(d.Body)
924
925 if ctx.currentBlock != nil && !terminates {
926 ctx.addInstr(&ir.Instruction{Op: ir.OpJmp, Args: []ir.Value{endLabel}})
927 }
928
929 ctx.startBlock(endLabel)
930 return false
931}
932
933func (ctx *Context) findAllAutosInFunc(node *ast.Node, autoVars *[]autoVarInfo, definedNames map[string]bool) {
934 if node == nil {
935 return
936 }
937 if node.Type == ast.VarDecl {
938 varData := node.Data.(ast.VarDeclNode)
939 if !definedNames[varData.Name] {
940 definedNames[varData.Name] = true
941 var size int64
942 if varData.Type != nil && varData.Type.Kind != ast.TYPE_UNTYPED {
943 size = ctx.getSizeof(varData.Type)
944 } else {
945 if varData.IsVector {
946 dataSizeInWords := int64(0)
947 if varData.SizeExpr != nil {
948 folded := ast.FoldConstants(varData.SizeExpr)
949 if folded.Type != ast.Number {
950 util.Error(node.Tok, "Local vector size must be a constant expression")
951 }
952 dataSizeInWords = folded.Data.(ast.NumberNode).Value
953 } else if len(varData.InitList) == 1 && varData.InitList[0].Type == ast.String {
954 strLen := int64(len(varData.InitList[0].Data.(ast.StringNode).Value))
955 numBytes := strLen + 1
956 dataSizeInWords = (numBytes + int64(ctx.wordSize) - 1) / int64(ctx.wordSize)
957 } else {
958 dataSizeInWords = int64(len(varData.InitList))
959 }
960 size = int64(ctx.wordSize) + dataSizeInWords*int64(ctx.wordSize)
961 } else {
962 size = int64(ctx.wordSize)
963 }
964 }
965 *autoVars = append(*autoVars, autoVarInfo{Node: node, Size: size})
966 }
967 }
968
969 switch d := node.Data.(type) {
970 case ast.IfNode:
971 ctx.findAllAutosInFunc(d.ThenBody, autoVars, definedNames)
972 ctx.findAllAutosInFunc(d.ElseBody, autoVars, definedNames)
973 case ast.WhileNode:
974 ctx.findAllAutosInFunc(d.Body, autoVars, definedNames)
975 case ast.BlockNode:
976 for _, s := range d.Stmts {
977 ctx.findAllAutosInFunc(s, autoVars, definedNames)
978 }
979 case ast.MultiVarDeclNode:
980 for _, decl := range d.Decls {
981 ctx.findAllAutosInFunc(decl, autoVars, definedNames)
982 }
983 case ast.SwitchNode:
984 ctx.findAllAutosInFunc(d.Body, autoVars, definedNames)
985 case ast.CaseNode:
986 ctx.findAllAutosInFunc(d.Body, autoVars, definedNames)
987 case ast.DefaultNode:
988 ctx.findAllAutosInFunc(d.Body, autoVars, definedNames)
989 case ast.LabelNode:
990 ctx.findAllAutosInFunc(d.Stmt, autoVars, definedNames)
991 }
992}
993
994func (ctx *Context) codegenFuncDecl(node *ast.Node) {
995 d := node.Data.(ast.FuncDeclNode)
996 if d.Body != nil && d.Body.Type == ast.AsmStmt {
997 asmCode := d.Body.Data.(ast.AsmStmtNode).Code
998 ctx.inlineAsm += fmt.Sprintf(".globl %s\n%s:\n\t%s\n", d.Name, d.Name, asmCode)
999 return
1000 }
1001 if d.Body == nil {
1002 return
1003 }
1004
1005 irReturnType := ir.GetType(d.ReturnType, ctx.wordSize)
1006 fn := &ir.Func{
1007 Name: d.Name, ReturnType: irReturnType, AstReturnType: d.ReturnType,
1008 HasVarargs: d.HasVarargs, AstParams: d.Params, Node: node,
1009 }
1010 ctx.prog.Funcs = append(ctx.prog.Funcs, fn)
1011
1012 prevFunc := ctx.currentFunc
1013 ctx.currentFunc = fn
1014 defer func() { ctx.currentFunc = prevFunc }()
1015
1016 ctx.enterScope()
1017 defer ctx.exitScope()
1018
1019 ctx.tempCount = 0
1020 ctx.startBlock(&ir.Label{Name: "start"})
1021
1022 for i, p := range d.Params {
1023 var name string
1024 var typ *ast.BxType
1025 if d.IsTyped {
1026 paramData := p.Data.(ast.VarDeclNode)
1027 name, typ = paramData.Name, paramData.Type
1028 } else {
1029 name = p.Data.(ast.IdentNode).Name
1030 }
1031 paramVal := &ir.Temporary{Name: name, ID: i}
1032 fn.Params = append(fn.Params, &ir.Param{
1033 Name: name,
1034 Typ: ir.GetType(typ, ctx.wordSize),
1035 Val: paramVal,
1036 })
1037 }
1038
1039 var paramInfos []autoVarInfo
1040 for _, p := range d.Params {
1041 paramInfos = append(paramInfos, autoVarInfo{Node: p, Size: int64(ctx.wordSize)})
1042 }
1043 for i, j := 0, len(paramInfos)-1; i < j; i, j = i+1, j-1 {
1044 paramInfos[i], paramInfos[j] = paramInfos[j], paramInfos[i]
1045 }
1046
1047 definedInFunc := make(map[string]bool)
1048 for _, p := range d.Params {
1049 var name string
1050 if d.IsTyped {
1051 name = p.Data.(ast.VarDeclNode).Name
1052 } else {
1053 name = p.Data.(ast.IdentNode).Name
1054 }
1055 definedInFunc[name] = true
1056 }
1057 var autoVars []autoVarInfo
1058 ctx.findAllAutosInFunc(d.Body, &autoVars, definedInFunc)
1059
1060 for i, j := 0, len(autoVars)-1; i < j; i, j = i+1, j-1 {
1061 autoVars[i], autoVars[j] = autoVars[j], autoVars[i]
1062 }
1063
1064 allLocals := append(paramInfos, autoVars...)
1065
1066 var totalFrameSize int64
1067 for _, av := range allLocals {
1068 totalFrameSize += av.Size
1069 }
1070
1071 var framePtr ir.Value
1072 if totalFrameSize > 0 {
1073 align := int64(ctx.stackAlign)
1074 totalFrameSize = (totalFrameSize + align - 1) &^ (align - 1)
1075 framePtr = ctx.newTemp()
1076 ctx.addInstr(&ir.Instruction{
1077 Op: ir.OpAlloc,
1078 Typ: ir.GetType(nil, ctx.wordSize),
1079 Result: framePtr,
1080 Args: []ir.Value{&ir.Const{Value: totalFrameSize}},
1081 Align: ctx.stackAlign,
1082 })
1083 }
1084
1085 var currentOffset int64
1086 for i, local := range allLocals {
1087 isParam := i < len(paramInfos)
1088
1089 var name string
1090 var typ *ast.BxType
1091 var isVec bool
1092 if local.Node.Type == ast.Ident {
1093 name = local.Node.Data.(ast.IdentNode).Name
1094 if d.Name == "main" && isParam {
1095 originalIndex := -1
1096 for j, p := range d.Params {
1097 if p == local.Node {
1098 originalIndex = j
1099 break
1100 }
1101 }
1102 if originalIndex == 1 {
1103 isVec = true
1104 }
1105 }
1106 } else {
1107 varData := local.Node.Data.(ast.VarDeclNode)
1108 name, typ, isVec = varData.Name, varData.Type, varData.IsVector
1109 }
1110
1111 sym := ctx.addSymbol(name, symVar, typ, isVec, local.Node)
1112 sym.StackOffset = currentOffset
1113
1114 addr := ctx.newTemp()
1115 ctx.addInstr(&ir.Instruction{
1116 Op: ir.OpAdd,
1117 Typ: ir.GetType(nil, ctx.wordSize),
1118 Result: addr,
1119 Args: []ir.Value{framePtr, &ir.Const{Value: currentOffset}},
1120 })
1121 sym.IRVal = addr
1122
1123 if isParam {
1124 var origParamIndex int = -1
1125 for j, p := range d.Params {
1126 if p == local.Node {
1127 origParamIndex = j
1128 break
1129 }
1130 }
1131
1132 if origParamIndex != -1 {
1133 paramVal := fn.Params[origParamIndex].Val
1134 ctx.genStore(sym.IRVal, paramVal, typ)
1135 }
1136 } else {
1137 if isVec && (typ == nil || typ.Kind == ast.TYPE_UNTYPED) {
1138 storageAddr := ctx.newTemp()
1139 ctx.addInstr(&ir.Instruction{
1140 Op: ir.OpAdd,
1141 Typ: ir.GetType(nil, ctx.wordSize),
1142 Result: storageAddr,
1143 Args: []ir.Value{addr, &ir.Const{Value: int64(ctx.wordSize)}},
1144 })
1145 ctx.genStore(addr, storageAddr, nil)
1146 }
1147 }
1148 currentOffset += local.Size
1149 }
1150
1151 bodyTerminates := ctx.codegenStmt(d.Body)
1152
1153 if !bodyTerminates {
1154 if d.ReturnType != nil && d.ReturnType.Kind == ast.TYPE_VOID {
1155 ctx.addInstr(&ir.Instruction{Op: ir.OpRet})
1156 } else {
1157 ctx.addInstr(&ir.Instruction{Op: ir.OpRet, Args: []ir.Value{&ir.Const{Value: 0}}})
1158 }
1159 }
1160}
1161
1162func (ctx *Context) codegenGlobalConst(node *ast.Node) ir.Value {
1163 folded := ast.FoldConstants(node)
1164 switch folded.Type {
1165 case ast.Number: return &ir.Const{Value: folded.Data.(ast.NumberNode).Value}
1166 case ast.FloatNumber:
1167 typ := ir.GetType(folded.Typ, ctx.wordSize)
1168 return &ir.FloatConst{Value: folded.Data.(ast.FloatNumberNode).Value, Typ: typ}
1169 case ast.String: return ctx.addString(folded.Data.(ast.StringNode).Value)
1170 case ast.Nil: return &ir.Const{Value: 0}
1171 case ast.Ident:
1172 name := folded.Data.(ast.IdentNode).Name
1173 sym := ctx.findSymbol(name)
1174 if sym == nil {
1175 util.Error(node.Tok, "Undefined symbol '%s' in global initializer", name)
1176 return nil
1177 }
1178 // Try to evaluate as a constant expression (for enum constants)
1179 if val, ok := ctx.evalConstExpr(folded); ok {
1180 return &ir.Const{Value: val}
1181 }
1182 return sym.IRVal
1183 case ast.AddressOf:
1184 lval := folded.Data.(ast.AddressOfNode).LValue
1185 if lval.Type != ast.Ident {
1186 util.Error(lval.Tok, "Global initializer must be the address of a global symbol")
1187 return nil
1188 }
1189 name := lval.Data.(ast.IdentNode).Name
1190 sym := ctx.findSymbol(name)
1191 if sym == nil {
1192 util.Error(lval.Tok, "Undefined symbol '%s' in global initializer", name)
1193 return nil
1194 }
1195 return sym.IRVal
1196 default:
1197 util.Error(node.Tok, "Global initializer must be a constant expression")
1198 return nil
1199 }
1200}
1201
1202func (ctx *Context) codegenVarDecl(node *ast.Node) {
1203 d := node.Data.(ast.VarDeclNode)
1204 sym := ctx.findSymbol(d.Name)
1205 if sym == nil {
1206 if ctx.currentFunc == nil {
1207 sym = ctx.addSymbol(d.Name, symVar, d.Type, d.IsVector, node)
1208 } else {
1209 util.Error(node.Tok, "Internal error: symbol '%s' not found during declaration", d.Name)
1210 return
1211 }
1212 }
1213
1214 if ctx.currentFunc == nil {
1215 ctx.codegenGlobalVarDecl(d, sym)
1216 } else {
1217 ctx.codegenLocalVarDecl(d, sym)
1218 }
1219}
1220
1221func (ctx *Context) codegenLocalVarDecl(d ast.VarDeclNode, sym *symbol) {
1222 if len(d.InitList) == 0 {
1223 return
1224 }
1225
1226 if d.IsVector || (d.Type != nil && d.Type.Kind == ast.TYPE_ARRAY) {
1227 vectorPtr, _ := ctx.codegenExpr(&ast.Node{Type: ast.Ident, Data: ast.IdentNode{Name: d.Name}, Tok: sym.Node.Tok})
1228
1229 if len(d.InitList) == 1 && d.InitList[0].Type == ast.String {
1230 strVal := d.InitList[0].Data.(ast.StringNode).Value
1231 strLabel := ctx.addString(strVal)
1232 sizeToCopy := len(strVal) + 1
1233 ctx.addInstr(&ir.Instruction{
1234 Op: ir.OpBlit,
1235 Args: []ir.Value{strLabel, vectorPtr, &ir.Const{Value: int64(sizeToCopy)}},
1236 })
1237 } else {
1238 for i, initExpr := range d.InitList {
1239 offset := int64(i) * int64(ctx.wordSize)
1240 elemAddr := ctx.newTemp()
1241 ctx.addInstr(&ir.Instruction{
1242 Op: ir.OpAdd,
1243 Typ: ir.GetType(nil, ctx.wordSize),
1244 Result: elemAddr,
1245 Args: []ir.Value{vectorPtr, &ir.Const{Value: offset}},
1246 })
1247 rval, _ := ctx.codegenExpr(initExpr)
1248 ctx.genStore(elemAddr, rval, nil)
1249 }
1250 }
1251 return
1252 }
1253
1254 initExpr := d.InitList[0]
1255 varType := d.Type
1256 if d.IsDefine && initExpr.Typ != nil {
1257 varType = initExpr.Typ
1258 } else if (varType == nil || varType.Kind == ast.TYPE_UNTYPED) && initExpr.Typ != nil {
1259 varType = initExpr.Typ
1260 }
1261
1262 if sym.BxType == nil || sym.BxType.Kind == ast.TYPE_UNTYPED {
1263 sym.BxType = varType
1264 }
1265
1266 // Resolve named struct types to their actual definitions
1267 if varType != nil && varType.Kind != ast.TYPE_STRUCT && varType.Name != "" {
1268 if typeSym := ctx.findTypeSymbol(varType.Name); typeSym != nil && typeSym.BxType.Kind == ast.TYPE_STRUCT {
1269 varType = typeSym.BxType
1270 }
1271 }
1272
1273 if varType != nil && varType.Kind == ast.TYPE_STRUCT {
1274 rvalPtr, _ := ctx.codegenExpr(initExpr)
1275 lvalAddr := sym.IRVal
1276 size := ctx.getSizeof(varType)
1277 ctx.addInstr(&ir.Instruction{
1278 Op: ir.OpBlit,
1279 Args: []ir.Value{rvalPtr, lvalAddr, &ir.Const{Value: size}},
1280 })
1281 } else {
1282 rval, _ := ctx.codegenExpr(initExpr)
1283 ctx.genStore(sym.IRVal, rval, varType)
1284 }
1285}
1286
1287func (ctx *Context) codegenGlobalVarDecl(d ast.VarDeclNode, sym *symbol) {
1288 globalData := &ir.Data{
1289 Name: sym.IRVal.(*ir.Global).Name,
1290 Align: int(ctx.getAlignof(d.Type)),
1291 AstType: d.Type,
1292 }
1293
1294 if d.Type != nil && d.Type.Kind == ast.TYPE_STRUCT && len(d.InitList) == 0 {
1295 structSize := ctx.getSizeof(d.Type)
1296 if structSize > 0 {
1297 globalData.Items = append(globalData.Items, ir.DataItem{Typ: ir.TypeB, Count: int(structSize)})
1298 }
1299 if len(globalData.Items) > 0 {
1300 ctx.prog.Globals = append(ctx.prog.Globals, globalData)
1301 }
1302 return
1303 }
1304
1305 isUntypedStringVec := d.IsVector && (d.Type == nil || d.Type.Kind == ast.TYPE_UNTYPED) &&
1306 len(d.InitList) == 1 && d.InitList[0].Type == ast.String
1307
1308 var elemType ir.Type
1309 if isUntypedStringVec {
1310 elemType = ir.TypeB
1311 } else {
1312 elemType = ir.GetType(d.Type, ctx.wordSize)
1313 if d.Type != nil && d.Type.Kind == ast.TYPE_ARRAY {
1314 elemType = ir.GetType(d.Type.Base, ctx.wordSize)
1315 }
1316 }
1317
1318 var numElements int64
1319 var sizeNode *ast.Node
1320 if d.Type != nil && d.Type.Kind == ast.TYPE_ARRAY {
1321 sizeNode = d.Type.ArraySize
1322 } else if d.IsVector {
1323 sizeNode = d.SizeExpr
1324 }
1325
1326 if sizeNode != nil {
1327 if val, ok := ctx.evalConstExpr(sizeNode); ok {
1328 numElements = val
1329 } else {
1330 util.Error(sizeNode.Tok, "Global array size must be a constant expression")
1331 }
1332 } else {
1333 numElements = int64(len(d.InitList))
1334 }
1335 if numElements == 0 && !d.IsVector && len(d.InitList) == 0 {
1336 numElements = 1
1337 }
1338
1339 if len(d.InitList) > 0 {
1340 for _, init := range d.InitList {
1341 val := ctx.codegenGlobalConst(init)
1342 itemType := elemType
1343 if _, ok := val.(*ir.Global); ok {
1344 itemType = ir.TypePtr
1345 }
1346 globalData.Items = append(globalData.Items, ir.DataItem{Typ: itemType, Value: val})
1347 }
1348 initializedElements := int64(len(d.InitList))
1349 if numElements > initializedElements {
1350 globalData.Items = append(globalData.Items, ir.DataItem{Typ: elemType, Count: int(numElements - initializedElements)})
1351 }
1352 } else if numElements > 0 {
1353 globalData.Items = append(globalData.Items, ir.DataItem{Typ: elemType, Count: int(numElements)})
1354 }
1355
1356 if len(globalData.Items) > 0 {
1357 ctx.prog.Globals = append(ctx.prog.Globals, globalData)
1358 }
1359}