main.go raw
1 package main
2
3 import (
4 "fmt"
5 "os"
6 "strings"
7
8 llvm "bootstrap/llvmpure"
9 )
10
11 func main() {
12 llvmPath := "/usr/lib/libLLVM-19.so"
13 gluePath := ""
14
15 if len(os.Args) >= 2 {
16 gluePath = os.Args[1]
17 }
18
19 if err := llvm.Init(llvmPath, gluePath); err != nil {
20 fmt.Fprintf(os.Stderr, "init: %v\n", err)
21 os.Exit(1)
22 }
23
24 pass := 0
25 fail := 0
26 assert := func(name string, cond bool) {
27 if cond {
28 pass++
29 } else {
30 fail++
31 fmt.Fprintf(os.Stderr, "FAIL: %s\n", name)
32 }
33 }
34
35 // 6A: Context + Module + Types
36
37 ctx := llvm.NewContext()
38 assert("context not nil", !ctx.IsNil())
39
40 mod := ctx.NewModule("testmod")
41 assert("module not nil", !mod.IsNil())
42
43 mod.SetTarget("x86_64-unknown-linux-musl")
44 assert("target roundtrip", mod.Target() == "x86_64-unknown-linux-musl")
45
46 mod.SetDataLayout("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128")
47 assert("datalayout set", mod.DataLayout() != "")
48
49 modCtx := mod.Context()
50 assert("module context matches", !modCtx.IsNil())
51
52 // Integer types
53 i1 := ctx.Int1Type()
54 assert("i1 kind", i1.TypeKind() == llvm.IntegerTypeKind)
55 assert("i1 width", i1.IntTypeWidth() == 1)
56
57 i8 := ctx.Int8Type()
58 assert("i8 width", i8.IntTypeWidth() == 8)
59
60 i16 := ctx.Int16Type()
61 assert("i16 width", i16.IntTypeWidth() == 16)
62
63 i32 := ctx.Int32Type()
64 assert("i32 width", i32.IntTypeWidth() == 32)
65
66 i64 := ctx.Int64Type()
67 assert("i64 width", i64.IntTypeWidth() == 64)
68
69 i128 := ctx.IntType(128)
70 assert("i128 width", i128.IntTypeWidth() == 128)
71
72 // Float types
73 f32 := ctx.FloatType()
74 assert("float kind", f32.TypeKind() == llvm.FloatTypeKind)
75
76 f64 := ctx.DoubleType()
77 assert("double kind", f64.TypeKind() == llvm.DoubleTypeKind)
78
79 // Void type
80 voidTy := ctx.VoidType()
81 assert("void kind", voidTy.TypeKind() == llvm.VoidTypeKind)
82
83 // Function type: i32(i32, i32)
84 fnType := llvm.FunctionType(i32, []llvm.Type{i32, i32}, false)
85 assert("func kind", fnType.TypeKind() == llvm.FunctionTypeKind)
86 assert("func not vararg", !fnType.IsFunctionVarArg())
87 assert("func return type", fnType.ReturnType().IntTypeWidth() == 32)
88 assert("func param count", fnType.ParamTypesCount() == 2)
89 params := fnType.ParamTypes()
90 assert("func param[0] width", params[0].IntTypeWidth() == 32)
91 assert("func param[1] width", params[1].IntTypeWidth() == 32)
92
93 // Vararg function type
94 vaFnType := llvm.FunctionType(i32, []llvm.Type{i32}, true)
95 assert("vararg func", vaFnType.IsFunctionVarArg())
96
97 // Struct type
98 structTy := ctx.StructType([]llvm.Type{i32, i64}, false)
99 assert("struct kind", structTy.TypeKind() == llvm.StructTypeKind)
100 assert("struct not packed", !structTy.IsStructPacked())
101 assert("struct elem count", structTy.StructElementTypesCount() == 2)
102 elems := structTy.StructElementTypes()
103 assert("struct elem[0]", elems[0].IntTypeWidth() == 32)
104 assert("struct elem[1]", elems[1].IntTypeWidth() == 64)
105
106 // Packed struct
107 packedStruct := ctx.StructType([]llvm.Type{i8, i32}, true)
108 assert("packed struct", packedStruct.IsStructPacked())
109
110 // Named struct
111 namedStruct := ctx.StructCreateNamed("MyStruct")
112 assert("named struct name", namedStruct.StructName() == "MyStruct")
113 namedStruct.StructSetBody([]llvm.Type{i32, i64, i8}, false)
114 assert("named struct body count", namedStruct.StructElementTypesCount() == 3)
115
116 // Array type
117 arrTy := llvm.ArrayType(i32, 10)
118 assert("array kind", arrTy.TypeKind() == llvm.ArrayTypeKind)
119 assert("array length", arrTy.ArrayLength() == 10)
120 assert("array elem", arrTy.ElementType().IntTypeWidth() == 32)
121
122 // Pointer type
123 ptrTy := llvm.PointerType(i32, 0)
124 assert("pointer kind", ptrTy.TypeKind() == llvm.PointerTypeKind)
125 assert("pointer addrspace", ptrTy.PointerAddressSpace() == 0)
126
127 // Vector type
128 vecTy := llvm.VectorType(i32, 4)
129 assert("vector kind", vecTy.TypeKind() == llvm.VectorTypeKind)
130 assert("vector size", vecTy.VectorSize() == 4)
131
132 // 6B: Builder - build add(a,b) function
133
134 addFnType := llvm.FunctionType(i32, []llvm.Type{i32, i32}, false)
135 addFn := mod.AddFunction("add", addFnType)
136 assert("function added", !addFn.IsNil())
137 assert("function name", addFn.Name() == "add")
138 assert("function param count", addFn.ParamsCount() == 2)
139
140 entry := ctx.AddBasicBlock(addFn, "entry")
141 assert("basic block added", !entry.IsNil())
142
143 builder := ctx.NewBuilder()
144 builder.SetInsertPointAtEnd(entry)
145
146 a := addFn.Param(0)
147 b := addFn.Param(1)
148 a.SetName("a")
149 b.SetName("b")
150 assert("param a name", a.Name() == "a")
151 assert("param b name", b.Name() == "b")
152
153 sum := builder.CreateAdd(a, b, "sum")
154 assert("add instruction", !sum.IsNil())
155
156 builder.CreateRet(sum)
157
158 // Verify IR string contains expected content
159 ir := mod.String()
160 assert("IR contains add function", strings.Contains(ir, "define i32 @add"))
161 assert("IR contains add instruction", strings.Contains(ir, "add i32"))
162 assert("IR contains ret", strings.Contains(ir, "ret i32"))
163 fmt.Printf("Generated IR:\n%s\n", ir)
164
165 // 6C: Module inspection
166
167 lookedUp := mod.NamedFunction("add")
168 assert("named function lookup", !lookedUp.IsNil())
169 assert("looked up name matches", lookedUp.Name() == "add")
170
171 // Iterate functions
172 first := mod.FirstFunction()
173 assert("first function", first.Name() == "add")
174
175 // 6A continued: Constants
176
177 c42 := llvm.ConstInt(i32, 42, false)
178 assert("const int not nil", !c42.IsNil())
179 assert("const int is constant", c42.IsConstant())
180 assert("const int value", c42.ZExtValue() == 42)
181
182 cnull := llvm.ConstNull(i32)
183 assert("const null is null", cnull.IsNull())
184
185 cundef := llvm.Undef(i32)
186 assert("undef is undef", cundef.IsUndef())
187
188 // Global variable
189 gv := mod.AddGlobal(i32, "myGlobal")
190 assert("global added", !gv.IsNil())
191 gv.SetInitializer(c42)
192 gv.SetGlobalConstant(true)
193 assert("global is constant", gv.IsGlobalConstant())
194 gv.SetLinkage(llvm.InternalLinkage)
195
196 namedGlobal := mod.NamedGlobal("myGlobal")
197 assert("named global lookup", !namedGlobal.IsNil())
198
199 // Verify module
200 err := mod.VerifyModule()
201 assert("module verifies", err == nil)
202 if err != nil {
203 fmt.Fprintf(os.Stderr, "verify: %v\n", err)
204 }
205
206 // Attributes
207 noinlineKind := llvm.AttributeKindID("noinline")
208 assert("noinline attr kind > 0", noinlineKind > 0)
209 noinlineAttr := ctx.CreateEnumAttribute(noinlineKind, 0)
210 assert("enum attr not nil", !noinlineAttr.IsNil())
211
212 // MDKindID
213 dbgKind := ctx.MDKindID("dbg")
214 assert("dbg md kind >= 0", dbgKind >= 0)
215
216 // Basic block operations
217 bbCount := addFn.BasicBlocksCount()
218 assert("add has 1 basic block", bbCount == 1)
219
220 entryBB := addFn.EntryBasicBlock()
221 assert("entry block not nil", !entryBB.IsNil())
222
223 firstInstr := entryBB.FirstInstruction()
224 assert("first instr not nil", !firstInstr.IsNil())
225
226 lastInstr := entryBB.LastInstruction()
227 assert("last instr is terminator", !lastInstr.IsATerminatorInst().IsNil())
228
229 // Builder - more complex: if/then/else
230 selFnType := llvm.FunctionType(i32, []llvm.Type{i1, i32, i32}, false)
231 selFn := mod.AddFunction("myselect", selFnType)
232 selEntry := ctx.AddBasicBlock(selFn, "entry")
233 builder.SetInsertPointAtEnd(selEntry)
234 sel := builder.CreateSelect(selFn.Param(0), selFn.Param(1), selFn.Param(2), "sel")
235 builder.CreateRet(sel)
236
237 err = mod.VerifyModule()
238 assert("module with select verifies", err == nil)
239
240 // Struct GEP
241 structPtrFnType := llvm.FunctionType(i32, []llvm.Type{llvm.PointerType(structTy, 0)}, false)
242 gepFn := mod.AddFunction("getfield", structPtrFnType)
243 gepEntry := ctx.AddBasicBlock(gepFn, "entry")
244 builder.SetInsertPointAtEnd(gepEntry)
245 gep := builder.CreateStructGEP(structTy, gepFn.Param(0), 0, "field0ptr")
246 loaded := builder.CreateLoad(i32, gep, "field0")
247 builder.CreateRet(loaded)
248
249 err = mod.VerifyModule()
250 assert("module with GEP verifies", err == nil)
251
252 // Cleanup first module
253 builder.Dispose()
254 mod.Dispose()
255 ctx.Dispose()
256
257 // ---- Phase 6D: Target machine ----
258
259 llvm.InitializeAllTargetInfos()
260 llvm.InitializeAllTargets()
261 llvm.InitializeAllTargetMCs()
262 llvm.InitializeAllAsmParsers()
263 llvm.InitializeAllAsmPrinters()
264
265 defTriple := llvm.DefaultTargetTriple()
266 assert("default triple not empty", defTriple != "")
267 fmt.Printf("default triple: %s\n", defTriple)
268
269 target, err := llvm.GetTargetFromTriple("x86_64-unknown-linux-musl")
270 assert("target from triple", err == nil)
271 assert("target not nil", !target.IsNil())
272 fmt.Printf("target name: %s\n", target.Name())
273 assert("target name not empty", target.Name() != "")
274
275 tm := target.CreateTargetMachine("x86_64-unknown-linux-musl", "x86-64", "",
276 llvm.CodeGenLevelDefault, llvm.RelocPIC, llvm.CodeModelDefault)
277 assert("target machine not nil", !tm.IsNil())
278
279 tmTriple := tm.Triple()
280 assert("tm triple", tmTriple == "x86_64-unknown-linux-musl")
281
282 td := tm.CreateTargetData()
283 assert("target data not nil", !td.IsNil())
284 assert("pointer size 8", td.PointerSize() == 8)
285
286 tdStr := td.String()
287 assert("target data string not empty", tdStr != "")
288 fmt.Printf("target data: %s\n", tdStr)
289
290 // Type size queries
291 ctx2 := llvm.NewContext()
292 i32td := ctx2.Int32Type()
293 i64td := ctx2.Int64Type()
294 assert("i32 size 32 bits", td.TypeSizeInBits(i32td) == 32)
295 assert("i64 size 64 bits", td.TypeSizeInBits(i64td) == 64)
296 assert("i32 store size 4", td.TypeStoreSize(i32td) == 4)
297 assert("i64 alloc size 8", td.TypeAllocSize(i64td) == 8)
298
299 // Struct layout queries
300 structTd := ctx2.StructType([]llvm.Type{i32td, i64td}, false)
301 structSize := td.TypeAllocSize(structTd)
302 assert("struct size >= 12", structSize >= 12)
303 offset1 := td.ElementOffset(structTd, 1)
304 assert("struct elem 1 offset >= 4", offset1 >= 4)
305 fmt.Printf("struct{i32,i64} size=%d, offset[1]=%d\n", structSize, offset1)
306
307 // Emit object code to memory buffer
308 mod2 := ctx2.NewModule("emitmod")
309 mod2.SetTarget("x86_64-unknown-linux-musl")
310 mod2.SetDataLayout(tdStr)
311 addFnType2 := llvm.FunctionType(i32td, []llvm.Type{i32td, i32td}, false)
312 addFn2 := mod2.AddFunction("testadd", addFnType2)
313 entry2 := ctx2.AddBasicBlock(addFn2, "entry")
314 b2 := ctx2.NewBuilder()
315 b2.SetInsertPointAtEnd(entry2)
316 sum2 := b2.CreateAdd(addFn2.Param(0), addFn2.Param(1), "sum")
317 b2.CreateRet(sum2)
318
319 mb, emitErr := tm.EmitToMemoryBuffer(mod2, llvm.ObjectFile)
320 assert("emit to membuf", emitErr == nil)
321 if emitErr != nil {
322 fmt.Fprintf(os.Stderr, "emit: %v\n", emitErr)
323 } else {
324 objBytes := mb.Bytes()
325 assert("object code > 0 bytes", len(objBytes) > 0)
326 // ELF magic: 0x7f 'E' 'L' 'F'
327 assert("ELF magic", len(objBytes) >= 4 && objBytes[0] == 0x7f && objBytes[1] == 'E' && objBytes[2] == 'L' && objBytes[3] == 'F')
328 fmt.Printf("emitted %d bytes of ELF object code\n", len(objBytes))
329 mb.Dispose()
330 }
331
332 // Bitcode roundtrip
333 bcBuf := llvm.WriteBitcodeToMemoryBuffer(mod2)
334 assert("bitcode buf not nil", !bcBuf.IsNil())
335 bcBytes := bcBuf.Bytes()
336 assert("bitcode > 0 bytes", len(bcBytes) > 0)
337 // Bitcode magic: 'BC' 0xc0 0xde
338 assert("bitcode magic", len(bcBytes) >= 4 && bcBytes[0] == 'B' && bcBytes[1] == 'C')
339 fmt.Printf("bitcode: %d bytes\n", len(bcBytes))
340 bcBuf.Dispose()
341
342 b2.Dispose()
343 mod2.Dispose()
344
345 // ---- Phase 6E: Debug info ----
346
347 mod3 := ctx2.NewModule("debugmod")
348 mod3.SetTarget("x86_64-unknown-linux-musl")
349 mod3.SetDataLayout(tdStr)
350
351 dib := llvm.NewDIBuilder(mod3)
352 assert("dibuilder not nil", dib != nil)
353
354 cu := dib.CreateCompileUnit(llvm.DICompileUnit{
355 Language: llvm.DW_LANG_Go,
356 File: "test.go",
357 Dir: "/tmp",
358 Producer: "moxie",
359 })
360 assert("compile unit not nil", !cu.IsNil())
361
362 diFile := dib.CreateFile("test.go", "/tmp")
363 assert("di file not nil", !diFile.IsNil())
364
365 diInt := dib.CreateBasicType(llvm.DIBasicType{
366 Name: "int32",
367 SizeInBits: 32,
368 Encoding: llvm.DW_ATE_signed,
369 })
370 assert("di basic type not nil", !diInt.IsNil())
371
372 diPtr := dib.CreatePointerType(llvm.DIPointerType{
373 Pointee: diInt,
374 SizeInBits: 64,
375 Name: "*int32",
376 })
377 assert("di pointer type not nil", !diPtr.IsNil())
378
379 diFnType := dib.CreateSubroutineType(llvm.DISubroutineType{
380 Parameters: []llvm.Metadata{diInt, diInt, diInt},
381 })
382 assert("di subroutine type not nil", !diFnType.IsNil())
383
384 diFn := dib.CreateFunction(diFile, llvm.DIFunction{
385 Name: "myfunc",
386 LinkageName: "myfunc",
387 File: diFile,
388 Line: 10,
389 Type: diFnType,
390 LocalToUnit: true,
391 IsDefinition: true,
392 ScopeLine: 10,
393 })
394 assert("di function not nil", !diFn.IsNil())
395
396 // Create a function with debug info attached (needs a body for verifier)
397 diFnLLVM := mod3.AddFunction("myfunc", llvm.FunctionType(i32td, []llvm.Type{i32td, i32td}, false))
398 diFnLLVM.SetSubprogram(diFn)
399 sp := diFnLLVM.Subprogram()
400 assert("subprogram roundtrip", !sp.IsNil())
401 b3 := ctx2.NewBuilder()
402 diEntry := ctx2.AddBasicBlock(diFnLLVM, "entry")
403 b3.SetInsertPointAtEnd(diEntry)
404 diSum := b3.CreateAdd(diFnLLVM.Param(0), diFnLLVM.Param(1), "sum")
405 b3.CreateRet(diSum)
406 b3.Dispose()
407
408 dib.Finalize()
409
410 err = mod3.VerifyModule()
411 assert("debug module verifies", err == nil)
412 if err != nil {
413 fmt.Fprintf(os.Stderr, "debug verify: %v\n", err)
414 }
415
416 dib.Destroy()
417 mod3.Dispose()
418
419 td.Dispose()
420 tm.Dispose()
421 ctx2.Dispose()
422
423 fmt.Printf("%d/%d tests passed\n", pass, pass+fail)
424 if fail > 0 {
425 os.Exit(1)
426 }
427 }
428