Files

463 lines
19 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from stdint import *
import memhub
import string
import viperlib
# ============================================================
# mlirlite -- an MLIR (llvm dialect) text emission library, isomorphic to llvmlite name for name
#
# Design principles:
# * The internal model (LLVMType/Value/Function/BasicBlock/IRBuilder/Module...) is
# **isomorphic to includes/llvmlite name for name and signature for signature**, so on the VPC side it suffices to rename
# `llvmlite.` to `mlirlite.` in order to compile;
# * The difference lies only in the "printing" stage: TypePrint / ValuePrint / FunctionPrint /
# LLVMModulePrint emit MLIR text, while the builders still generate the intermediate instruction text as before;
# * Names keep the LLVMxxx form (isomorphism first). See README.md for semantic differences.
# ============================================================
# Import submodules
from .__types import (
LLVMType, ParamNode, LLVMTypeCore,
Int1, Int8, Int16, Int32, Int64, Ptr, Func, Void, Array, Struct,
Half, Float, Double, FP128, Label,
new_param_node, param_list_append,
paramnode_get_ty, paramnode_get_next, paramnode_set_ty, paramnode_set_next,
TypePrint, PrintStructDefinition, get_struct_name,
get_struct_fields, get_struct_fcount, DeepCopyType,
)
from .__values import (
Value, new_value, ConstInt, ConstNull, ConstZero, ConstFloat, SSAValue, ValuePrint,
value_get_ty, value_get_next,
value_set_ty, value_set_next, value_set_name, value_set_isconst,
)
from .__function import (
Line, BasicBlock, Param, Function,
new_line, new_basic_block, new_param, new_function,
function_add_param, function_add_block,
block_append_line, block_append_text,
function_set_attrs, param_set_attrs,
function_set_attr_group,
function_set_dbg,
function_get_name, function_get_name_len, function_get_ret_ty, function_get_params,
function_get_param_head, function_get_next, function_is_declared,
param_get_name, param_get_ty, param_get_next,
FunctionPrint, G_PeepEnable,
DebugSetEnabled, DebugIsEnabled, DebugNoteFunc, DebugClearFunc,
DebugNoteLine, DebugResetFunc, DebugTakeText,
)
from .__module import (
LLVMModule, GlobalVariable, NamedTypeNode, DINode, DINamedMeta,
new_module, new_global_variable,
module_add_function, module_add_global, module_add_named_type,
module_set_named_type_packed,
module_set_target, module_set_datalayout,
module_add_di_node, module_add_named_metadata,
global_set_constant, global_set_linkage, global_set_unnamed_addr, global_set_section,
LLVMModulePrint, OUTPUT_FULL, OUTPUT_STUB, OUTPUT_TEXT,
module_ensure_opaque_for_type,
)
from .__builder import (
IRBuilder, new_builder, position_at_end,
builder_cur_block_is_terminated,
builder_set_debug_location, builder_clear_debug_location,
build_alloca, build_alloca_aligned, build_alloca_at_entry, build_load, build_store,
build_store_at_entry,
build_add, build_sub, build_mul, build_sdiv, build_udiv, build_srem, build_urem,
build_and, build_or, build_xor, build_shl, build_lshr, build_ashr,
build_fadd, build_fsub, build_fmul, build_fdiv, build_frem, build_fneg,
build_icmp, build_fcmp,
build_br, build_cond_br, build_ret, build_ret_void,
build_call, build_call_indirect, build_bitcast, build_sext, build_trunc, build_zext,
build_ptrtoint, build_inttoptr,
build_fpext, build_fptrunc, build_fp2si, build_si2fp, build_fp2ui, build_ui2fp,
build_gep, build_gep_array, build_gep_struct,
build_phi, build_switch, build_select, build_unreachable,
build_atomicrmw, build_cmpxchg, build_inline_asm, build_va_arg,
PhiIncoming, new_phi_incoming, SwitchCase, new_switch_case,
ICMP_EQ, ICMP_NE, ICMP_SGT, ICMP_SGE, ICMP_SLT, ICMP_SLE,
ICMP_UGT, ICMP_UGE, ICMP_ULT, ICMP_ULE,
FCMP_FALSE, FCMP_OEQ, FCMP_OGT, FCMP_OGE, FCMP_OLT, FCMP_OLE,
FCMP_ONE, FCMP_ORD, FCMP_UNO, FCMP_UEQ, FCMP_UGT, FCMP_UGE,
FCMP_ULT, FCMP_ULE, FCMP_UNE, FCMP_TRUE,
ATOMIC_XCHG, ATOMIC_ADD, ATOMIC_SUB, ATOMIC_AND, ATOMIC_NAND,
ATOMIC_OR, ATOMIC_XOR, ATOMIC_MAX, ATOMIC_MIN, ATOMIC_UMAX, ATOMIC_UMIN,
ATOMIC_ORDER_NOTATOMIC, ATOMIC_ORDER_UNORDERED, ATOMIC_ORDER_MONOTONIC,
ATOMIC_ORDER_ACQUIRE, ATOMIC_ORDER_RELEASE, ATOMIC_ORDER_ACQ_REL,
ATOMIC_ORDER_SEQ_CST,
)
from .__verify import (
VerifyResult, NameEntry,
verify_function, verify_module,
VERIFY_OK, VERIFY_ERR_NO_TERMINATOR, VERIFY_ERR_DUPLICATE_DEF,
VERIFY_ERR_UNDEFINED_USE, VERIFY_ERR_PHI_NOT_FIRST,
_new_verify_result,
)
# ============================================================
# Convenience factory: string copy
# ============================================================
def _str_dup(pool: memhub.MemBuddy*, src: str) -> str:
"""Allocate from pool and copy a C string."""
if src is None: return None
slen: sizet = string.strlen(src)
buf: str = pool.alloc(slen + 1)
if buf is None: return None
string.strcpy(buf, src)
return buf
# ============================================================
# Convenience factory: create a named basic block
# ============================================================
def create_block(pool: memhub.MemBuddy*, func: Function*,
name: str) -> BasicBlock*:
"""Create a basic block and add it to the function."""
# Block-name/param conflict guard: block label "entry:" is value %entry, so a param %entry
# collides; on collision append .1/.2/... (strip a leading % from Param.Name before comparing).
use_name: str = name
if func is not None and func.Params is not None and name is not None \
and name[0] != '\0':
cb_len: sizet = string.strlen(name) + 16
cb_buf: str = pool.alloc(cb_len)
if cb_buf is not None:
cb_k: int = 1
cb_collide: int = 1
while cb_collide != 0:
cb_collide = 0
cb_p: Param* = function_get_param_head(func)
while cb_p is not None:
cb_pnm: str = param_get_name(cb_p)
if cb_pnm is not None:
cb_pv: str = cb_pnm
if cb_pv[0] == '%':
cb_pv = cb_pv + 1
if string.strcmp(cb_pv, use_name) == 0:
cb_collide = 1
break
cb_p = param_get_next(cb_p)
if cb_collide != 0:
viperlib.snprintf(cb_buf, cb_len, "%s.%d", name, cb_k)
use_name = cb_buf
cb_k += 1
name_dup: str = _str_dup(pool, use_name)
blk: BasicBlock* = new_basic_block(pool, name_dup)
if blk is not None:
function_add_block(func, blk)
return blk
# ============================================================
# Convenience factory: create an integer constant value
# ============================================================
def const_int(pool: memhub.MemBuddy*, bits: int,
val: int64) -> Value*:
"""Create an integer constant value (auto-creates type + name text).
bits: bit width (1/8/16/32/64); val: integer value.
"""
# create type
ty: LLVMType* = pool.alloc(LLVMType.__sizeof__())
if ty is None: return None
string.memset(ty, 0, LLVMType.__sizeof__())
*ty = *LLVMType.Int(bits)
# create name text
name: str = pool.alloc(32)
if name is None: return None
# LLVM i64 literals exclude INT64_MIN: represent it as the unsigned bit pattern
# 9223372036854775808 (accepted by LLVM), else opt rejects "-9223372036854775808".
if bits == 64 and val == -9223372036854775807 - 1:
viperlib.snprintf(name, 32, "9223372036854775808")
else:
viperlib.snprintf(name, 32, "%lld", val)
return ConstInt(pool, ty, val, name)
def const_int32(pool: memhub.MemBuddy*, val: int64) -> Value*:
"""Convenience: create an i32 integer constant."""
return const_int(pool, 32, val)
def const_int64(pool: memhub.MemBuddy*, val: int64) -> Value*:
"""Convenience: create an i64 integer constant."""
return const_int(pool, 64, val)
# ============================================================
# Convenience factory: create a function and add it to the module
# ============================================================
def create_function(pool: memhub.MemBuddy*, mod: LLVMModule*,
name: str, ret_ty: LLVMType*) -> Function*:
"""Create a function and add it to the module."""
name_dup: str = _str_dup(pool, name)
func: Function* = new_function(pool, name_dup, ret_ty)
if func is not None:
module_add_function(mod, func)
return func
def add_param(pool: memhub.MemBuddy*, func: Function*,
ty: LLVMType*, name: str) -> Param*:
"""Create a parameter and add it to the function."""
name_dup: str = _str_dup(pool, name)
param: Param* = new_param(pool, ty, name_dup)
if param is not None:
function_add_param(func, param)
return param
# ============================================================
# Convenience factory: create a function declaration (declare)
# ============================================================
def create_declare(pool: memhub.MemBuddy*, mod: LLVMModule*,
name: str, ret_ty: LLVMType*) -> Function*:
"""Create a function declaration (declare, no body) and add it to the module.
Unlike create_function, it emits only: declare <ret_ty> @<name>(<params>)
"""
name_dup: str = _str_dup(pool, name)
func: Function* = new_function(pool, name_dup, ret_ty)
if func is not None:
func.IsDeclared = 1
module_add_function(mod, func)
return func
# ============================================================
# Convenience factory: create a string-constant global variable
#
# The initializer is stored in gv.Initializer as llvmlite-shaped LLVM syntax text (c"contents\00");
# the MLIR render stage (__module.vp/_MlirGlobalInit) converts it to the attribute form "contents".
# ============================================================
def create_global_string(pool: memhub.MemBuddy*, mod: LLVMModule*,
name: str, text: str) -> GlobalVariable*:
"""Create a string-constant global variable and add it to the module.
name: global name (e.g. ".str"); text: C string without trailing NUL (added automatically).
Returns: GlobalVariable pointer of type [len+1 x i8].
"""
if text is None: return None
slen: sizet = string.strlen(text)
# 显式收窄:LLVMType.Array 的 Count 是 int(i32),而 slen 是 sizet(u64);
# 这里按显式转换(非隐式窄化)取 i32(字符串长度远超 2^31 不现实)。
count: int = int(slen + 1) # includes the trailing NUL
# create i8 type
i8_ty: LLVMType* = pool.alloc(LLVMType.__sizeof__())
if i8_ty is None: return None
string.memset(i8_ty, 0, LLVMType.__sizeof__())
*i8_ty = *LLVMType.Int(8)
# create [count x i8] array type
arr_ty: LLVMType* = pool.alloc(LLVMType.__sizeof__())
if arr_ty is None: return None
string.memset(arr_ty, 0, LLVMType.__sizeof__())
*arr_ty = *LLVMType.Array(i8_ty, count)
# create global variable
name_dup: str = _str_dup(pool, name)
gv: GlobalVariable* = new_global_variable(pool, name_dup, arr_ty)
if gv is None: return None
# set attributes: private + unnamed_addr + constant
global_set_linkage(gv, "private")
global_set_unnamed_addr(gv, 1)
global_set_constant(gv, 1)
# Build initializer text c"contents\00": LLVM IR string literal c"<chars>" with " \ and
# non-printables escaped as \xx; buffer sized for worst case (4 bytes/char) + c"..." + \00 + "
init_buf: str = pool.alloc(slen * 4 + 8)
if init_buf is None: return None
pos: sizet = 0
init_buf[pos] = 'c'
pos += 1
init_buf[pos] = '"'
pos += 1
for i in range(int(slen)):
ch: uint8 = text[i]
if ch == 0x22 or ch == 0x5C or ch < 0x20 or ch >= 0x7F:
# escape: " -> \22, \ -> \5c, non-printable -> \xx
init_buf[pos] = '\\'
pos += 1
hi: int = (ch >> 4) & 0x0F
lo: int = ch & 0x0F
if hi < 10:
init_buf[pos] = char(0x30 + hi)
else:
init_buf[pos] = char(0x61 + hi - 10)
pos += 1
if lo < 10:
init_buf[pos] = char(0x30 + lo)
else:
init_buf[pos] = char(0x61 + lo - 10)
pos += 1
else:
init_buf[pos] = ch
pos += 1
# trailing NUL byte
init_buf[pos] = '\\'
pos += 1
init_buf[pos] = '0'
pos += 1
init_buf[pos] = '0'
pos += 1
init_buf[pos] = '"'
pos += 1
init_buf[pos] = '\0'
gv.Initializer = init_buf
module_add_global(mod, gv)
return gv
# ============================================================
# Convenience factory: create an integer global with an initializer
# ============================================================
def create_global_int(pool: memhub.MemBuddy*, mod: LLVMModule*,
name: str, bits: int, val: int64) -> GlobalVariable*:
"""Create an integer global variable and add it to the module.
MLIR output: llvm.mlir.global @<name>(<val> : i<bits>) : i<bits>
"""
# create integer type
ty: LLVMType* = pool.alloc(LLVMType.__sizeof__())
if ty is None: return None
string.memset(ty, 0, LLVMType.__sizeof__())
*ty = *LLVMType.Int(bits)
name_dup: str = _str_dup(pool, name)
gv: GlobalVariable* = new_global_variable(pool, name_dup, ty)
if gv is None: return None
# build initializer text "42" (the type is printed separately by the global render stage)
init_buf: str = pool.alloc(48)
if init_buf is None: return None
viperlib.snprintf(init_buf, 48, "%lld", val)
gv.Initializer = init_buf
module_add_global(mod, gv)
return gv
# ============================================================
# LLVMModuleCore: module/function/value creation core; wraps pool to avoid passing it repeatedly
#
# Usage:
# mcore: rawptr[LLVMModuleCore] = LLVMModuleCore(pool)
# mod: rawptr[LLVMModule] = mcore.NewModule("test")
# func: rawptr[Function] = mcore.CreateFunction(mod, "add", ty_i32)
# val: rawptr[Value] = mcore.ConstInt32(42)
# ============================================================
novirtual class LLVMModuleCore:
"""Module/function/value creation core; wraps pool so callers need not pass it."""
Pool: memhub.MemBuddy*
def __init__(self, pool: memhub.MemBuddy*):
self.Pool = pool
# ---- module operations ----
def NewModule(self, name: str) -> LLVMModule*:
return new_module(self.Pool, name)
def CreateFunction(self, mod: LLVMModule*, name: str,
ret_ty: LLVMType*) -> Function*:
return create_function(self.Pool, mod, name, ret_ty)
def CreateDeclare(self, mod: LLVMModule*, name: str,
ret_ty: LLVMType*) -> Function*:
"""Create a function declaration (declare, no body)."""
return create_declare(self.Pool, mod, name, ret_ty)
def AddParam(self, func: Function*, ty: LLVMType*,
name: str) -> Param*:
return add_param(self.Pool, func, ty, name)
def FunctionSetAttrs(self, func: Function*, attrs: str):
"""Set the function-level attribute text."""
function_set_attrs(func, attrs)
def ParamSetAttrs(self, param: Param*, attrs: str):
"""Set the parameter attribute text."""
param_set_attrs(param, attrs)
def CreateBlock(self, func: Function*,
name: str) -> BasicBlock*:
return create_block(self.Pool, func, name)
def NewBuilder(self, func: Function*) -> IRBuilder*:
return new_builder(self.Pool, func)
# ---- globals ----
def NewGlobal(self, mod: LLVMModule*, name: str,
ty: LLVMType*) -> GlobalVariable*:
"""Create a global variable and add it to the module."""
name_dup: str = _str_dup(self.Pool, name)
gv: GlobalVariable* = new_global_variable(self.Pool, name_dup, ty)
if gv is not None:
module_add_global(mod, gv)
return gv
def CreateGlobalString(self, mod: LLVMModule*, name: str,
text: str) -> GlobalVariable*:
"""Create a string-constant global variable."""
return create_global_string(self.Pool, mod, name, text)
def CreateGlobalInt(self, mod: LLVMModule*, name: str,
bits: int, val: int64) -> GlobalVariable*:
"""Create an integer global with an initializer."""
return create_global_int(self.Pool, mod, name, bits, val)
def GlobalSetConstant(self, gv: GlobalVariable*, is_const: int):
global_set_constant(gv, is_const)
def GlobalSetLinkage(self, gv: GlobalVariable*, linkage: str):
global_set_linkage(gv, linkage)
def GlobalSetUnnamedAddr(self, gv: GlobalVariable*, level: int):
global_set_unnamed_addr(gv, level)
def GlobalSetSection(self, gv: GlobalVariable*, section: str):
global_set_section(gv, section)
# ---- value creation ----
def ConstInt(self, bits: int, val: int64) -> Value*:
return const_int(self.Pool, bits, val)
def ConstInt32(self, val: int64) -> Value*:
return const_int32(self.Pool, val)
def ConstInt64(self, val: int64) -> Value*:
return const_int64(self.Pool, val)
def ConstFloat(self, ty: LLVMType*, val: double) -> Value*:
return ConstFloat(self.Pool, ty, val)
def SSAValue(self, ty: LLVMType*, name: str) -> Value*:
return SSAValue(self.Pool, ty, name)
def ConstNull(self, ty: LLVMType*, name: str) -> Value*:
return ConstNull(self.Pool, ty, name)
# ---- printing ----
def PrintValue(self, buf: str, size: sizet, val: Value*):
ValuePrint(buf, size, val, self.Pool)
def PrintFunction(self, buf: str, size: sizet, func: Function*):
FunctionPrint(buf, size, func, self.Pool)
def PrintModule(self, buf: str, size: sizet, mod: LLVMModule*):
LLVMModulePrint(buf, size, mod, self.Pool, OUTPUT_FULL)
# ---- SSA verification ----
def NewVerifyResult(self, buf_size: sizet) -> VerifyResult*:
"""Create a verify-result object (buf_size = error message buffer size)."""
return _new_verify_result(self.Pool, buf_size)
def VerifyFunction(self, func: Function*,
result: VerifyResult*) -> int:
"""Verify SSA correctness of one function; returns error count (0 = pass)."""
return verify_function(self.Pool, func, result)
def VerifyModule(self, mod: LLVMModule*,
result: VerifyResult*) -> int:
"""Verify SSA correctness of all functions in the module; returns error count (0 = pass)."""
return verify_module(self.Pool, mod, result)