mirror of
https://github.com/LLVMParty/llvm-nanobind
synced 2026-06-21 13:43:38 +00:00
5.8 KiB
5.8 KiB
llvm-nanobind Cheat Sheet
Quick reference for common operations. Keep this open while coding!
Module Operations
# Create from scratch
with llvm.create_context() as ctx:
with ctx.create_module("name") as mod:
...
# Parse existing IR
with llvm.create_context() as ctx:
with ctx.parse_ir(ir_text) as mod:
...
# Output
ir_string = mod.to_string()
is_valid = mod.verify()
error = mod.verification_error
Types
# Integer types
i1 = ctx.types.i1 # bool
i8 = ctx.types.i8 # byte
i32 = ctx.types.i32 # int
i64 = ctx.types.i64 # long
# Pointer
ptr = ctx.types.ptr
# Composite types
arr = ctx.types.array(elem_ty, count)
fn = ctx.types.function(ret_ty, [param_ty, ...])
struct = ctx.types.struct([field_ty, ...])
# Type properties
ty.kind == llvm.TypeKind.Integer
ty.int_width # for integers
Functions
# Create
fn = mod.add_function("name", fn_type)
fn.get_param(0).name = "arg_name"
# Iterate
for func in mod.functions:
if func.is_declaration:
continue
# func is a definition
Basic Blocks
# Create
bb = func.append_basic_block("name")
# Iterate
for bb in func.basic_blocks:
term = bb.terminator # last instruction
for inst in bb.instructions:
...
# Movement
bb.move_before(other_bb)
bb.move_after(other_bb)
Instructions
# Properties
inst.opcode == llvm.Opcode.Add
inst.is_terminator
inst.name
inst.type
bb = inst.block # explicit parent block
bb = inst.parent # alias
# Operands
for op in inst.operands:
...
inst.set_operand(i, new_val) # indexed mutation
# Uses
for use in inst.uses:
user = use.user
# user is an instruction that uses inst
# Deletion
inst.erase_from_parent()
Builder
# Create
with bb.create_builder() as builder:
...
# Positioning
builder.position_at_end(bb)
builder.position_before(inst)
# Arithmetic
builder.add(a, b, "name")
builder.sub(a, b, "name")
builder.mul(a, b, "name")
builder.neg(a, "name")
builder.not_(a, "name") # underscore!
builder.and_(a, b, "name") # underscore!
builder.or_(a, b, "name")
builder.xor(a, b, "name")
# Memory
builder.alloca(ty, name="name")
builder.load(ty, ptr, "name")
builder.store(val, ptr)
builder.gep(ty, ptr, indices, "name")
# Control flow
builder.br(dest_bb)
builder.cond_br(cond, true_bb, false_bb)
builder.ret(val)
builder.ret_void()
# Comparison
builder.icmp(llvm.IntPredicate.EQ, a, b, "name")
# Predicates: EQ, NE, UGT, UGE, ULT, ULE, SGT, SGE, SLT, SLE
# PHI
phi = builder.phi(ty, "name")
phi.add_incoming(val, from_bb)
Constants
# Integers
c = i32.constant(42)
c = i32.constant(-1) # signed OK
# Strings and raw bytes
c = ctx.const_string("text", dont_null_terminate=False)
raw = ctx.const_string(b"\xff\x80B", dont_null_terminate=True)
# Null pointer
null = llvm.ConstantPointerNull.get(ptr_ty)
# Block address (for indirect branches)
addr = bb.block_address()
PHI Nodes
# Create
phi = builder.phi(ty, "name")
phi.add_incoming(val1, from_bb1)
phi.add_incoming(val2, from_bb2)
# Read
for i in range(phi.num_incoming):
val = phi.get_incoming_value(i)
bb = phi.get_incoming_block(i)
Globals
# Create
gv = mod.add_global(ty, "name")
gv.initializer = const_val
gv.linkage = llvm.Linkage.Private
gv.is_global_constant = True
# Iterate
for gv in mod.globals:
init = gv.initializer # None if no initializer
ty = gv.global_value_type # type of contents
Common Patterns
Replace All Uses With
old_val.replace_all_uses_with(new_val)
Safe Instruction Replacement
def replace_instruction(old_inst, new_val):
"""Replace instruction with new value and delete."""
old_inst.replace_all_uses_with(new_val)
old_inst.erase_from_parent()
Collect Then Modify
# When modifying while iterating: collect first!
to_process = []
for bb in func.basic_blocks:
for inst in bb.instructions:
if should_process(inst):
to_process.append(inst)
for inst in to_process:
transform(inst)
Find Instructions by Opcode
def find_by_opcode(func, opcode):
result = []
for bb in func.basic_blocks:
for inst in bb.instructions:
if inst.opcode == opcode:
result.append(inst)
return result
subs = find_by_opcode(func, llvm.Opcode.Sub)
Gotchas Reference
| What you try | What happens | Fix |
|---|---|---|
mod = ctx.parse_ir(...) |
Gets manager, not module | Use with ... as mod |
bb.terminator on unterminated block |
Raises LLVMAssertionError |
Check bb.has_terminator first |
inst.set_operand(i, value_of_wrong_type) |
Raises LLVMAssertionError |
Use a value with the exact expected type |
ctx.const_string("\xff") for raw bytes |
Uses text/UTF-8 path | Pass bytes: b"\xff" |
Opcodes Reference
Terminators: Ret, Br, Switch, IndirectBr, Invoke, Unreachable
Binary: Add, Sub, Mul, UDiv, SDiv, URem, SRem
FAdd, FSub, FMul, FDiv, FRem
Bitwise: Shl, LShr, AShr, And, Or, Xor
Memory: Alloca, Load, Store, GetElementPtr, Fence
AtomicCmpXchg, AtomicRMW
Casts: Trunc, ZExt, SExt, FPToUI, FPToSI
UIToFP, SIToFP, FPTrunc, FPExt
PtrToInt, IntToPtr, BitCast, AddrSpaceCast
Other: ICmp, FCmp, PHI, Call, Select, VAArg
ExtractElement, InsertElement, ShuffleVector
ExtractValue, InsertValue, LandingPad
Quick Debugging
# Print IR
print(mod.to_string())
print(func) # just the function
print(inst) # just the instruction
# Check what you have
print(type(thing))
print(dir(thing))
# Verify validity
if not mod.verify():
print(mod.verification_error)
Keep this handy while you code!