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