Blob Blame History Raw
From 6053c1c8528c63a669d070550bacd43f2f6a68e1 Mon Sep 17 00:00:00 2001
From: Ayke van Laethem <aykevanlaethem@gmail.com>
Date: Tue, 20 Sep 2022 23:26:49 +0200
Subject: [PATCH 12/26] all: replace llvm.Const* calls with builder.Create*
 calls

A number of llvm.Const* functions (in particular extractvalue and
insertvalue) were removed in LLVM 15, so we have to use a builder
instead. This builder will create the same constant values, it simply
uses a different API.

Signed-off-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
---
 compiler/asserts.go             |  2 +-
 compiler/compiler.go            | 23 ++++++++++++++++-------
 compiler/interface.go           | 20 ++++++++++----------
 compiler/interrupt.go           |  8 +++++---
 compiler/llvm.go                |  2 +-
 interp/interpreter.go           | 13 +++++++------
 interp/memory.go                |  8 ++++----
 transform/gc.go                 |  2 +-
 transform/interface-lowering.go | 14 +++++++-------
 transform/interrupt.go          |  7 ++++---
 transform/llvm.go               | 16 +++++++++-------
 transform/reflect.go            | 32 +++++++++++++++++++-------------
 transform/rtcalls.go            |  4 ++--
 13 files changed, 86 insertions(+), 65 deletions(-)

diff --git a/compiler/asserts.go b/compiler/asserts.go
index 8d9efdde..2a5265e9 100644
--- a/compiler/asserts.go
+++ b/compiler/asserts.go
@@ -145,7 +145,7 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value,
 	}
 	// Make the maxBufSize actually the maximum allowed value (in number of
 	// elements in the channel buffer).
-	maxBufSize = llvm.ConstUDiv(maxBufSize, llvm.ConstInt(b.uintptrType, elementSize, false))
+	maxBufSize = b.CreateUDiv(maxBufSize, llvm.ConstInt(b.uintptrType, elementSize, false), "")
 
 	// Make sure maxBufSize has the same type as bufSize.
 	if maxBufSize.Type() != bufSize.Type() {
diff --git a/compiler/compiler.go b/compiler/compiler.go
index c93b2e56..3d29628c 100644
--- a/compiler/compiler.go
+++ b/compiler/compiler.go
@@ -63,6 +63,7 @@ type compilerContext struct {
 	DumpSSA          bool
 	mod              llvm.Module
 	ctx              llvm.Context
+	builder          llvm.Builder // only used for constant operations
 	dibuilder        *llvm.DIBuilder
 	cu               llvm.Metadata
 	difiles          map[string]llvm.Metadata
@@ -98,6 +99,7 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
 	}
 
 	c.ctx = llvm.NewContext()
+	c.builder = c.ctx.NewBuilder()
 	c.mod = c.ctx.NewModule(moduleName)
 	c.mod.SetTarget(config.Triple)
 	c.mod.SetDataLayout(c.targetData.String())
@@ -126,6 +128,12 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
 	return c
 }
 
+// Dispose everything related to the context, _except_ for the IR module (and
+// the associated context).
+func (c *compilerContext) dispose() {
+	c.builder.Dispose()
+}
+
 // builder contains all information relevant to build a single function.
 type builder struct {
 	*compilerContext
@@ -256,6 +264,7 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
 // CompilePackage compiles a single package to a LLVM module.
 func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
 	c := newCompilerContext(moduleName, machine, config, dumpSSA)
+	defer c.dispose()
 	c.packageDir = pkg.OriginalDir()
 	c.embedGlobals = pkg.EmbedGlobals
 	c.pkg = pkg.Pkg
@@ -972,10 +981,10 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
 		for _, file := range allFiles {
 			fileStruct := llvm.ConstNull(embedFileStructType)
 			name := c.createConst(ssa.NewConst(constant.MakeString(file.Name), types.Typ[types.String]))
-			fileStruct = llvm.ConstInsertValue(fileStruct, name, []uint32{0}) // "name" field
+			fileStruct = c.builder.CreateInsertValue(fileStruct, name, 0, "") // "name" field
 			if file.Hash != "" {
 				data := c.getEmbedFileString(file)
-				fileStruct = llvm.ConstInsertValue(fileStruct, data, []uint32{1}) // "data" field
+				fileStruct = c.builder.CreateInsertValue(fileStruct, data, 1, "") // "data" field
 			}
 			fileStructs = append(fileStructs, fileStruct)
 		}
@@ -1006,7 +1015,7 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
 		// Define the embed.FS struct. It has only one field: the files (as a
 		// *[]embed.file).
 		globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem()))
-		globalInitializer = llvm.ConstInsertValue(globalInitializer, sliceGlobal, []uint32{0})
+		globalInitializer = c.builder.CreateInsertValue(globalInitializer, sliceGlobal, 0, "")
 		global.SetInitializer(globalInitializer)
 		global.SetVisibility(llvm.HiddenVisibility)
 		global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
@@ -2757,15 +2766,15 @@ func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
 			r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
 			i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
 			cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
-			cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
-			cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
+			cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
+			cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
 			return cplx
 		} else if typ.Kind() == types.Complex128 {
 			r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
 			i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
 			cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
-			cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
-			cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
+			cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
+			cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
 			return cplx
 		} else {
 			panic("unknown constant of basic type: " + expr.String())
diff --git a/compiler/interface.go b/compiler/interface.go
index acb474f5..106c327a 100644
--- a/compiler/interface.go
+++ b/compiler/interface.go
@@ -88,20 +88,20 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
 		}
 		globalValue := llvm.ConstNull(global.Type().ElementType())
 		if !references.IsNil() {
-			globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
+			globalValue = c.builder.CreateInsertValue(globalValue, references, 0, "")
 		}
 		if length != 0 {
 			lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
-			globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
+			globalValue = c.builder.CreateInsertValue(globalValue, lengthValue, 1, "")
 		}
 		if !methodSet.IsNil() {
-			globalValue = llvm.ConstInsertValue(globalValue, methodSet, []uint32{2})
+			globalValue = c.builder.CreateInsertValue(globalValue, methodSet, 2, "")
 		}
 		if !ptrTo.IsNil() {
-			globalValue = llvm.ConstInsertValue(globalValue, ptrTo, []uint32{3})
+			globalValue = c.builder.CreateInsertValue(globalValue, ptrTo, 3, "")
 		}
 		if !typeAssert.IsNil() {
-			globalValue = llvm.ConstInsertValue(globalValue, typeAssert, []uint32{4})
+			globalValue = c.builder.CreateInsertValue(globalValue, typeAssert, 4, "")
 		}
 		global.SetInitializer(globalValue)
 		global.SetLinkage(llvm.LinkOnceODRLinkage)
@@ -121,7 +121,7 @@ func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
 	structGlobalValue := llvm.ConstNull(structGlobalType)
 	for i := 0; i < typ.NumFields(); i++ {
 		fieldGlobalValue := llvm.ConstNull(runtimeStructField)
-		fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, c.getTypeCode(typ.Field(i).Type()), []uint32{0})
+		fieldGlobalValue = c.builder.CreateInsertValue(fieldGlobalValue, c.getTypeCode(typ.Field(i).Type()), 0, "")
 		fieldNameType, fieldName := c.makeGlobalArray([]byte(typ.Field(i).Name()), "reflect/types.structFieldName", c.ctx.Int8Type())
 		fieldName.SetLinkage(llvm.PrivateLinkage)
 		fieldName.SetUnnamedAddr(true)
@@ -129,7 +129,7 @@ func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
 			llvm.ConstInt(c.ctx.Int32Type(), 0, false),
 			llvm.ConstInt(c.ctx.Int32Type(), 0, false),
 		})
-		fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldName, []uint32{1})
+		fieldGlobalValue = c.builder.CreateInsertValue(fieldGlobalValue, fieldName, 1, "")
 		if typ.Tag(i) != "" {
 			fieldTagType, fieldTag := c.makeGlobalArray([]byte(typ.Tag(i)), "reflect/types.structFieldTag", c.ctx.Int8Type())
 			fieldTag.SetLinkage(llvm.PrivateLinkage)
@@ -138,13 +138,13 @@ func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
 				llvm.ConstInt(c.ctx.Int32Type(), 0, false),
 				llvm.ConstInt(c.ctx.Int32Type(), 0, false),
 			})
-			fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldTag, []uint32{2})
+			fieldGlobalValue = c.builder.CreateInsertValue(fieldGlobalValue, fieldTag, 2, "")
 		}
 		if typ.Field(i).Embedded() {
 			fieldEmbedded := llvm.ConstInt(c.ctx.Int1Type(), 1, false)
-			fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldEmbedded, []uint32{3})
+			fieldGlobalValue = c.builder.CreateInsertValue(fieldGlobalValue, fieldEmbedded, 3, "")
 		}
-		structGlobalValue = llvm.ConstInsertValue(structGlobalValue, fieldGlobalValue, []uint32{uint32(i)})
+		structGlobalValue = c.builder.CreateInsertValue(structGlobalValue, fieldGlobalValue, i, "")
 	}
 	structGlobal.SetInitializer(structGlobalValue)
 	structGlobal.SetUnnamedAddr(true)
diff --git a/compiler/interrupt.go b/compiler/interrupt.go
index 45c7d074..c1f7d69f 100644
--- a/compiler/interrupt.go
+++ b/compiler/interrupt.go
@@ -49,9 +49,11 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
 	global.SetGlobalConstant(true)
 	global.SetUnnamedAddr(true)
 	initializer := llvm.ConstNull(globalLLVMType)
-	initializer = llvm.ConstInsertValue(initializer, funcContext, []uint32{0})
-	initializer = llvm.ConstInsertValue(initializer, funcPtr, []uint32{1})
-	initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(b.intType, uint64(id.Int64()), true), []uint32{2, 0})
+	initializer = b.CreateInsertValue(initializer, funcContext, 0, "")
+	initializer = b.CreateInsertValue(initializer, funcPtr, 1, "")
+	initializer = b.CreateInsertValue(initializer, llvm.ConstNamedStruct(globalLLVMType.StructElementTypes()[2], []llvm.Value{
+		llvm.ConstInt(b.intType, uint64(id.Int64()), true),
+	}), 2, "")
 	global.SetInitializer(initializer)
 
 	// Add debug info to the interrupt global.
diff --git a/compiler/llvm.go b/compiler/llvm.go
index 686d4d2b..8b80869b 100644
--- a/compiler/llvm.go
+++ b/compiler/llvm.go
@@ -70,7 +70,7 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l
 	value := llvm.Undef(globalType)
 	for i := 0; i < len(buf); i++ {
 		ch := uint64(buf[i])
-		value = llvm.ConstInsertValue(value, llvm.ConstInt(elementType, ch, false), []uint32{uint32(i)})
+		value = c.builder.CreateInsertValue(value, llvm.ConstInt(elementType, ch, false), i, "")
 	}
 	global.SetInitializer(value)
 	return globalType, global
diff --git a/interp/interpreter.go b/interp/interpreter.go
index 2c7fd8c3..d029aa33 100644
--- a/interp/interpreter.go
+++ b/interp/interpreter.go
@@ -408,7 +408,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
 				// Elem() is only valid for certain type classes.
 				switch class {
 				case "chan", "pointer", "slice", "array":
-					elementType := llvm.ConstExtractValue(typecodeID.Initializer(), []uint32{0})
+					elementType := r.builder.CreateExtractValue(typecodeID.Initializer(), 0, "")
 					uintptrType := r.mod.Context().IntType(int(mem.r.pointerSize) * 8)
 					locals[inst.localIndex] = r.getValue(llvm.ConstPtrToInt(elementType, uintptrType))
 				default:
@@ -461,8 +461,8 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
 				// easier checking in the next step.
 				concreteTypeMethods := map[string]struct{}{}
 				for i := 0; i < methodSet.Type().ArrayLength(); i++ {
-					methodInfo := llvm.ConstExtractValue(methodSet, []uint32{uint32(i)})
-					name := llvm.ConstExtractValue(methodInfo, []uint32{0}).Name()
+					methodInfo := r.builder.CreateExtractValue(methodSet, i, "")
+					name := r.builder.CreateExtractValue(methodInfo, 0, "").Name()
 					concreteTypeMethods[name] = struct{}{}
 				}
 
@@ -496,7 +496,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
 				typecodeID := typecodeIDBitCast.Operand(0).Initializer()
 
 				// Load the method set, which is part of the typecodeID object.
-				methodSet := llvm.ConstExtractValue(typecodeID, []uint32{2}).Operand(0).Initializer()
+				methodSet := r.builder.CreateExtractValue(typecodeID, 2, "").Operand(0).Initializer()
 
 				// We don't need to load the interface method set.
 
@@ -511,9 +511,10 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
 				numMethods := methodSet.Type().ArrayLength()
 				var method llvm.Value
 				for i := 0; i < numMethods; i++ {
-					methodSignature := llvm.ConstExtractValue(methodSet, []uint32{uint32(i), 0})
+					methodSignatureAgg := r.builder.CreateExtractValue(methodSet, i, "")
+					methodSignature := r.builder.CreateExtractValue(methodSignatureAgg, 0, "")
 					if methodSignature == signature {
-						method = llvm.ConstExtractValue(methodSet, []uint32{uint32(i), 1}).Operand(0)
+						method = r.builder.CreateExtractValue(methodSignatureAgg, 1, "").Operand(0)
 					}
 				}
 				if method.IsNil() {
diff --git a/interp/memory.go b/interp/memory.go
index 2fa6f5cb..583f9355 100644
--- a/interp/memory.go
+++ b/interp/memory.go
@@ -215,7 +215,7 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
 		case llvm.StructTypeKind:
 			numElements := llvmType.StructElementTypesCount()
 			for i := 0; i < numElements; i++ {
-				element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
+				element := mv.r.builder.CreateExtractValue(llvmValue, i, "")
 				err := mv.markExternal(element, mark)
 				if err != nil {
 					return err
@@ -224,7 +224,7 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
 		case llvm.ArrayTypeKind:
 			numElements := llvmType.ArrayLength()
 			for i := 0; i < numElements; i++ {
-				element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
+				element := mv.r.builder.CreateExtractValue(llvmValue, i, "")
 				err := mv.markExternal(element, mark)
 				if err != nil {
 					return err
@@ -1074,7 +1074,7 @@ func (v *rawValue) set(llvmValue llvm.Value, r *runner) {
 				field := rawValue{
 					buf: v.buf[offset:],
 				}
-				field.set(llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)}), r)
+				field.set(r.builder.CreateExtractValue(llvmValue, i, ""), r)
 			}
 		case llvm.ArrayTypeKind:
 			numElements := llvmType.ArrayLength()
@@ -1085,7 +1085,7 @@ func (v *rawValue) set(llvmValue llvm.Value, r *runner) {
 				field := rawValue{
 					buf: v.buf[offset:],
 				}
-				field.set(llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)}), r)
+				field.set(r.builder.CreateExtractValue(llvmValue, i, ""), r)
 			}
 		case llvm.DoubleTypeKind:
 			f, _ := llvmValue.DoubleValue()
diff --git a/transform/gc.go b/transform/gc.go
index 3870a6b6..514fb1bf 100644
--- a/transform/gc.go
+++ b/transform/gc.go
@@ -218,7 +218,7 @@ func MakeGCStackSlots(mod llvm.Module) bool {
 		initialStackObject := llvm.ConstNull(stackObjectType)
 		numSlots := (targetData.TypeAllocSize(stackObjectType) - uint64(targetData.PointerSize())*2) / uint64(targetData.ABITypeAlignment(uintptrType))
 		numSlotsValue := llvm.ConstInt(uintptrType, numSlots, false)
-		initialStackObject = llvm.ConstInsertValue(initialStackObject, numSlotsValue, []uint32{1})
+		initialStackObject = builder.CreateInsertValue(initialStackObject, numSlotsValue, 1, "")
 		builder.CreateStore(initialStackObject, stackObject)
 
 		// Update stack start.
diff --git a/transform/interface-lowering.go b/transform/interface-lowering.go
index f5b29e30..f28443dc 100644
--- a/transform/interface-lowering.go
+++ b/transform/interface-lowering.go
@@ -154,7 +154,7 @@ func (p *lowerInterfacesPass) run() error {
 				if initializer.IsNil() {
 					continue
 				}
-				methodSet := llvm.ConstExtractValue(initializer, []uint32{2})
+				methodSet := p.builder.CreateExtractValue(initializer, 2, "")
 				p.addTypeMethods(t, methodSet)
 			}
 		}
@@ -288,9 +288,9 @@ func (p *lowerInterfacesPass) run() error {
 	zeroUintptr := llvm.ConstNull(p.uintptrType)
 	for _, t := range p.types {
 		initializer := t.typecode.Initializer()
-		methodSet := llvm.ConstExtractValue(initializer, []uint32{2})
-		initializer = llvm.ConstInsertValue(initializer, llvm.ConstNull(methodSet.Type()), []uint32{2})
-		initializer = llvm.ConstInsertValue(initializer, zeroUintptr, []uint32{4})
+		methodSet := p.builder.CreateExtractValue(initializer, 2, "")
+		initializer = p.builder.CreateInsertValue(initializer, llvm.ConstNull(methodSet.Type()), 2, "")
+		initializer = p.builder.CreateInsertValue(initializer, zeroUintptr, 4, "")
 		t.typecode.SetInitializer(initializer)
 	}
 
@@ -311,10 +311,10 @@ func (p *lowerInterfacesPass) addTypeMethods(t *typeInfo, methodSet llvm.Value)
 	t.methodSet = methodSet
 	set := methodSet.Initializer() // get value from global
 	for i := 0; i < set.Type().ArrayLength(); i++ {
-		methodData := llvm.ConstExtractValue(set, []uint32{uint32(i)})
-		signatureGlobal := llvm.ConstExtractValue(methodData, []uint32{0})
+		methodData := p.builder.CreateExtractValue(set, i, "")
+		signatureGlobal := p.builder.CreateExtractValue(methodData, 0, "")
 		signatureName := signatureGlobal.Name()
-		function := llvm.ConstExtractValue(methodData, []uint32{1}).Operand(0)
+		function := p.builder.CreateExtractValue(methodData, 1, "").Operand(0)
 		signature := p.getSignature(signatureName)
 		method := &methodInfo{
 			function:      function,
diff --git a/transform/interrupt.go b/transform/interrupt.go
index 2c301be0..b15ff8a9 100644
--- a/transform/interrupt.go
+++ b/transform/interrupt.go
@@ -44,7 +44,8 @@ func LowerInterrupts(mod llvm.Module) []error {
 
 			// Get the interrupt number from the initializer
 			initializer := global.Initializer()
-			num := llvm.ConstExtractValue(initializer, []uint32{2, 0}).SExtValue()
+			interrupt := builder.CreateExtractValue(initializer, 2, "")
+			num := builder.CreateExtractValue(interrupt, 0, "").SExtValue()
 			pkg := packageFromInterruptHandle(global)
 
 			handles, exists := handleMap[num]
@@ -89,8 +90,8 @@ func LowerInterrupts(mod llvm.Module) []error {
 			builder.SetInsertPointBefore(call)
 			for _, handler := range handlers {
 				initializer := handler.Initializer()
-				context := llvm.ConstExtractValue(initializer, []uint32{0})
-				funcPtr := llvm.ConstExtractValue(initializer, []uint32{1}).Operand(0)
+				context := builder.CreateExtractValue(initializer, 0, "")
+				funcPtr := builder.CreateExtractValue(initializer, 1, "").Operand(0)
 				builder.CreateCall(funcPtr.GlobalValueType(), funcPtr, []llvm.Value{
 					num,
 					context,
diff --git a/transform/llvm.go b/transform/llvm.go
index 6716dd63..17968f8a 100644
--- a/transform/llvm.go
+++ b/transform/llvm.go
@@ -36,25 +36,27 @@ func hasUses(value llvm.Value) bool {
 // linkage/constant/etc properties yourself.
 func makeGlobalArray(mod llvm.Module, bufItf interface{}, name string, elementType llvm.Type) (llvm.Type, llvm.Value) {
 	buf := reflect.ValueOf(bufItf)
-	globalType := llvm.ArrayType(elementType, buf.Len())
-	global := llvm.AddGlobal(mod, globalType, name)
-	value := llvm.Undef(globalType)
+	var values []llvm.Value
 	for i := 0; i < buf.Len(); i++ {
 		ch := buf.Index(i).Uint()
-		value = llvm.ConstInsertValue(value, llvm.ConstInt(elementType, ch, false), []uint32{uint32(i)})
+		values = append(values, llvm.ConstInt(elementType, ch, false))
 	}
+	value := llvm.ConstArray(elementType, values)
+	global := llvm.AddGlobal(mod, value.Type(), name)
 	global.SetInitializer(value)
-	return globalType, global
+	return value.Type(), global
 }
 
 // getGlobalBytes returns the slice contained in the array of the provided
 // global. It can recover the bytes originally created using makeGlobalArray, if
 // makeGlobalArray was given a byte slice.
-func getGlobalBytes(global llvm.Value) []byte {
+//
+// The builder parameter is only used for constant operations.
+func getGlobalBytes(global llvm.Value, builder llvm.Builder) []byte {
 	value := global.Initializer()
 	buf := make([]byte, value.Type().ArrayLength())
 	for i := range buf {
-		buf[i] = byte(llvm.ConstExtractValue(value, []uint32{uint32(i)}).ZExtValue())
+		buf[i] = byte(builder.CreateExtractValue(value, i, "").ZExtValue())
 	}
 	return buf
 }
diff --git a/transform/reflect.go b/transform/reflect.go
index fd70ccb7..68beba9b 100644
--- a/transform/reflect.go
+++ b/transform/reflect.go
@@ -76,6 +76,10 @@ var nonBasicTypes = map[string]int64{
 // typeCodeAssignmentState keeps some global state around for type code
 // assignments, used to assign one unique type code to each Go type.
 type typeCodeAssignmentState struct {
+	// Builder used purely for constant operations (because LLVM 15 removed many
+	// llvm.Const* functions).
+	builder llvm.Builder
+
 	// An integer that's incremented each time it's used to give unique IDs to
 	// type codes that are not yet fully supported otherwise by the reflect
 	// package (or are simply unused in the compiled program).
@@ -165,6 +169,7 @@ func LowerReflect(mod llvm.Module) {
 	defer targetData.Dispose()
 	uintptrType := mod.Context().IntType(targetData.PointerSize() * 8)
 	state := typeCodeAssignmentState{
+		builder:                          mod.Context().NewBuilder(),
 		fallbackIndex:                    1,
 		uintptrLen:                       targetData.PointerSize() * 8,
 		namedBasicTypes:                  make(map[string]int),
@@ -177,6 +182,7 @@ func LowerReflect(mod llvm.Module) {
 		needsStructNamesSidetable:        len(getUses(mod.NamedGlobal("reflect.structNamesSidetable"))) != 0,
 		needsArrayTypesSidetable:         len(getUses(mod.NamedGlobal("reflect.arrayTypesSidetable"))) != 0,
 	}
+	defer state.builder.Dispose()
 	for _, t := range types {
 		num := state.getTypeCodeNum(t.typecode)
 		if num.BitLen() > state.uintptrLen || !num.IsUint64() {
@@ -238,7 +244,7 @@ func LowerReflect(mod llvm.Module) {
 	// It also cleans up the IR for testing.
 	for _, typ := range types {
 		initializer := typ.typecode.Initializer()
-		references := llvm.ConstExtractValue(initializer, []uint32{0})
+		references := state.builder.CreateExtractValue(initializer, 0, "")
 		typ.typecode.SetInitializer(llvm.ConstNull(initializer.Type()))
 		if strings.HasPrefix(typ.name, "reflect/types.type:struct:") {
 			// Structs have a 'references' field that is not a typecode but
@@ -260,7 +266,7 @@ func (state *typeCodeAssignmentState) getTypeCodeNum(typecode llvm.Value) *big.I
 	name := ""
 	if class == "named" {
 		name = value
-		typecode = llvm.ConstExtractValue(typecode.Initializer(), []uint32{0})
+		typecode = state.builder.CreateExtractValue(typecode.Initializer(), 0, "")
 		class, value = getClassAndValueFromTypeCode(typecode)
 	}
 	if class == "basic" {
@@ -344,7 +350,7 @@ func (state *typeCodeAssignmentState) getNonBasicTypeCode(class string, typecode
 	switch class {
 	case "chan", "pointer", "slice":
 		// Prefix-style type kinds. The upper bits contain the element type.
-		sub := llvm.ConstExtractValue(typecode.Initializer(), []uint32{0})
+		sub := state.builder.CreateExtractValue(typecode.Initializer(), 0, "")
 		return state.getTypeCodeNum(sub)
 	case "array":
 		// An array is basically a pair of (typecode, length) stored in a
@@ -416,7 +422,7 @@ func (state *typeCodeAssignmentState) getArrayTypeNum(typecode llvm.Value) int {
 		return num
 	}
 
-	elemTypeCode := llvm.ConstExtractValue(typecode.Initializer(), []uint32{0})
+	elemTypeCode := state.builder.CreateExtractValue(typecode.Initializer(), 0, "")
 	elemTypeNum := state.getTypeCodeNum(elemTypeCode)
 	if elemTypeNum.BitLen() > state.uintptrLen || !elemTypeNum.IsUint64() {
 		// TODO: make this a regular error
@@ -424,7 +430,7 @@ func (state *typeCodeAssignmentState) getArrayTypeNum(typecode llvm.Value) int {
 	}
 
 	// The array side table is a sequence of {element type, array length}.
-	arrayLength := llvm.ConstExtractValue(typecode.Initializer(), []uint32{1}).ZExtValue()
+	arrayLength := state.builder.CreateExtractValue(typecode.Initializer(), 1, "").ZExtValue()
 	buf := makeVarint(elemTypeNum.Uint64())
 	buf = append(buf, makeVarint(arrayLength)...)
 
@@ -454,7 +460,7 @@ func (state *typeCodeAssignmentState) getStructTypeNum(typecode llvm.Value) int
 
 	// Get the fields this struct type contains.
 	// The struct number will be the start index of
-	structTypeGlobal := llvm.ConstExtractValue(typecode.Initializer(), []uint32{0}).Operand(0).Initializer()
+	structTypeGlobal := state.builder.CreateExtractValue(typecode.Initializer(), 0, "").Operand(0).Initializer()
 	numFields := structTypeGlobal.Type().ArrayLength()
 
 	// The first data that is stored in the struct sidetable is the number of
@@ -471,28 +477,28 @@ func (state *typeCodeAssignmentState) getStructTypeNum(typecode llvm.Value) int
 	// the sidetable bigger.
 	for i := 0; i < numFields; i++ {
 		// Collect some information about this field.
-		field := llvm.ConstExtractValue(structTypeGlobal, []uint32{uint32(i)})
+		field := state.builder.CreateExtractValue(structTypeGlobal, i, "")
 
-		nameGlobal := llvm.ConstExtractValue(field, []uint32{1})
+		nameGlobal := state.builder.CreateExtractValue(field, 1, "")
 		if nameGlobal == llvm.ConstPointerNull(nameGlobal.Type()) {
 			panic("compiler: no name for this struct field")
 		}
-		fieldNameBytes := getGlobalBytes(nameGlobal.Operand(0))
+		fieldNameBytes := getGlobalBytes(nameGlobal.Operand(0), state.builder)
 		fieldNameNumber := state.getStructNameNumber(fieldNameBytes)
 
 		// See whether this struct field has an associated tag, and if so,
 		// store that tag in the tags sidetable.
-		tagGlobal := llvm.ConstExtractValue(field, []uint32{2})
+		tagGlobal := state.builder.CreateExtractValue(field, 2, "")
 		hasTag := false
 		tagNumber := 0
 		if tagGlobal != llvm.ConstPointerNull(tagGlobal.Type()) {
 			hasTag = true
-			tagBytes := getGlobalBytes(tagGlobal.Operand(0))
+			tagBytes := getGlobalBytes(tagGlobal.Operand(0), state.builder)
 			tagNumber = state.getStructNameNumber(tagBytes)
 		}
 
 		// The 'embedded' or 'anonymous' flag for this field.
-		embedded := llvm.ConstExtractValue(field, []uint32{3}).ZExtValue() != 0
+		embedded := state.builder.CreateExtractValue(field, 3, "").ZExtValue() != 0
 
 		// The first byte in the struct types sidetable is a flags byte with
 		// two bits in it.
@@ -510,7 +516,7 @@ func (state *typeCodeAssignmentState) getStructTypeNum(typecode llvm.Value) int
 
 		// Get the type number and add it to the buffer.
 		// All fields have a type, so include it directly here.
-		typeNum := state.getTypeCodeNum(llvm.ConstExtractValue(field, []uint32{0}))
+		typeNum := state.getTypeCodeNum(state.builder.CreateExtractValue(field, 0, ""))
 		if typeNum.BitLen() > state.uintptrLen || !typeNum.IsUint64() {
 			// TODO: make this a regular error
 			panic("struct field has a type code that is too big")
diff --git a/transform/rtcalls.go b/transform/rtcalls.go
index 2f17f815..d70bc626 100644
--- a/transform/rtcalls.go
+++ b/transform/rtcalls.go
@@ -149,7 +149,7 @@ func OptimizeReflectImplements(mod llvm.Module) {
 		interfaceType := interfaceTypeBitCast.Operand(0)
 		if strings.HasPrefix(interfaceType.Name(), "reflect/types.type:named:") {
 			// Get the underlying type.
-			interfaceType = llvm.ConstExtractValue(interfaceType.Initializer(), []uint32{0})
+			interfaceType = builder.CreateExtractValue(interfaceType.Initializer(), 0, "")
 		}
 		if !strings.HasPrefix(interfaceType.Name(), "reflect/types.type:interface:") {
 			// This is an error. The Type passed to Implements should be of
@@ -161,7 +161,7 @@ func OptimizeReflectImplements(mod llvm.Module) {
 			// Interface is unknown at compile time. This can't be optimized.
 			continue
 		}
-		typeAssertFunction := llvm.ConstExtractValue(interfaceType.Initializer(), []uint32{4}).Operand(0)
+		typeAssertFunction := builder.CreateExtractValue(interfaceType.Initializer(), 4, "").Operand(0)
 
 		// Replace Implements call with the type assert call.
 		builder.SetInsertPointBefore(call)
-- 
2.38.1