Skip to content

Commit 5732f66

Browse files
committed
[lang] Inline PTX support for LLVMIR backend
Signed-off-by: Greg Bonik <gbonik@nvidia.com>
1 parent 87a2420 commit 5732f66

5 files changed

Lines changed: 97 additions & 9 deletions

File tree

‎experimental/cuda-lang/src/cuda/lang/_ir/op_defs.py‎

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from __future__ import annotations
66

7+
import itertools
78
from dataclasses import dataclass
89
from typing import Optional, Any, TYPE_CHECKING
910

@@ -22,6 +23,7 @@
2223
from cuda.tile._memory_model import MemoryScope
2324
from cuda.tile._ir.ir import MemoryEffect, add_operation_variadic
2425
from cuda.tile._ir.type import TensorLikeTy
26+
from cuda.lang import _datatype as datatype
2527
from cuda.lang._enums import VectorReduction
2628
from .ir import Operation, Var, attribute, operand
2729
from .type import Type, VectorTy, ScalarTy, PointerTy
@@ -83,8 +85,7 @@ def generate_llvm(self, ctx):
8385
elif len(self.result_vars) == 1:
8486
return (result,)
8587
else:
86-
return tuple(ctx.builder.extract_value(ctx.typeof(r), result, i)
87-
for i, r in enumerate(self.result_vars))
88+
return ctx.unpack_struct(result)
8889

8990

9091
def call_intrinsic(stub, *args: Var):
@@ -200,6 +201,71 @@ class InlinePTX(Operation, opcode="inline_ptx", memory_effect=MemoryEffect.STORE
200201
text: tuple[InlineAsmPiece, ...] = attribute()
201202
inputs: tuple[Var, ...] = operand()
202203

204+
@override
205+
def generate_llvm(self, ctx):
206+
num_outputs = len(self.result_vars)
207+
llvm_types = []
208+
constraints = []
209+
for i, var in enumerate(itertools.chain(self.result_vars, self.inputs)):
210+
ty = var.get_type()
211+
assert ty.tensor_shape() == ()
212+
dtype = ty.tensor_dtype()
213+
code = _dtype_to_inline_ptx_constraint(dtype)
214+
prefix = "=" if i < num_outputs else ""
215+
constraints.append(prefix + code)
216+
llvm_types.append(ctx.typeof(var))
217+
218+
pieces = []
219+
for p in self.text:
220+
if isinstance(p, str):
221+
pieces.append(p)
222+
else:
223+
if isinstance(p, InlineAsmInput):
224+
linear_index = num_outputs + p.index
225+
else:
226+
assert isinstance(p, InlineAsmOutput)
227+
linear_index = p.index
228+
pieces.append(f"${linear_index}")
229+
230+
tt = ctx.builder.type_table
231+
if num_outputs == 0:
232+
ret_ty = tt.VOID
233+
elif num_outputs == 1:
234+
ret_ty = llvm_types[0]
235+
else:
236+
ret_ty = tt.struct_anonymous(llvm_types[:num_outputs])
237+
238+
func_ty = tt.function(ret_ty, llvm_types[num_outputs:])
239+
240+
asm = ctx.builder.constants.inline_asm(func_ty, "".join(pieces), ",".join(constraints),
241+
side_effects=True)
242+
r = ctx.builder.call(func_ty, asm, [ctx.value(x) for x in self.inputs])
243+
244+
if num_outputs == 0:
245+
return ()
246+
elif num_outputs == 1:
247+
return (r,)
248+
else:
249+
return ctx.unpack_struct(r)
250+
251+
252+
def _dtype_to_inline_ptx_constraint(dtype: datatype.DType) -> str:
253+
if dtype == datatype.float32:
254+
return "f"
255+
elif dtype == datatype.float64:
256+
return "d"
257+
elif dtype == datatype.bool_:
258+
return "b"
259+
elif dtype.bitwidth == 16:
260+
return "h"
261+
elif dtype.bitwidth == 32:
262+
return "r"
263+
elif dtype.bitwidth == 64:
264+
return "l"
265+
elif dtype.bitwidth == 128:
266+
return "q"
267+
raise NotImplementedError(f"Can't map dtype {dtype} to InlineAsm constraint")
268+
203269

204270
@dataclass(eq=False)
205271
class Fence(Operation, opcode="fence", memory_effect=MemoryEffect.STORE):

‎experimental/cuda-lang/src/cuda/lang/_llvm_bitcode/__init__.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@
1212
)
1313

1414
from ._builder import (
15+
AnonStructType,
1516
BitcodeBuilder,
17+
FloatType,
1618
Function,
1719
FunctionType,
20+
IntegerType,
1821
Metadata,
1922
MetadataTable,
23+
PointerType,
2024
Type,
2125
TypeTable,
2226
Value,
@@ -36,11 +40,15 @@
3640
"CmpPredicate",
3741
"FloatKind",
3842
"Linkage",
43+
"AnonStructType",
3944
"BitcodeBuilder",
45+
"FloatType",
4046
"Function",
4147
"FunctionType",
48+
"IntegerType",
4249
"Metadata",
4350
"MetadataTable",
51+
"PointerType",
4452
"Type",
4553
"TypeTable",
4654
"Value",

‎experimental/cuda-lang/src/cuda/lang/_llvm_bitcode/_builder.py‎

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,8 @@ def is_declaration(self) -> bool:
366366
@dataclass
367367
class GlobalVariable:
368368
name: str
369-
value: Value
369+
value: Value # Has a pointer type
370+
value_type: Type # Actual type of the global
370371
address_space: int
371372
is_constant: bool
372373
initializer: Value | None
@@ -428,10 +429,11 @@ def global_variable(self,
428429
linkage: Linkage = Linkage.External,
429430
alignment: int | None = None) -> Value:
430431
assert self._cur_function is None, "Global variables must be put at the global scope"
431-
value = Value(type)
432+
value = Value(self.type_table.pointer(address_space))
432433
self._global_variables.append(GlobalVariable(
433434
name=name,
434435
value=value,
436+
value_type=type,
435437
address_space=address_space,
436438
is_constant=is_constant,
437439
initializer=initializer,
@@ -524,9 +526,16 @@ def get_element_ptr(self, element_ty: Type, ptr: Value, *indices: Value) -> Valu
524526
*indices
525527
)
526528

527-
def extract_value(self, result_ty: Type, src: Value, *indices: int) -> Value:
529+
def extract_value(self, src: Value, *indices: int) -> Value:
530+
ty = src.type
531+
for i in indices:
532+
if isinstance(ty, AnonStructType):
533+
ty = ty.fields[i]
534+
else:
535+
raise NotImplementedError()
536+
528537
return self._instruction(
529-
result_ty,
538+
ty,
530539
codes.FUNC_CODE_INST_EXTRACTVAL,
531540
"V" + "i" * len(indices),
532541
src, *indices
@@ -804,7 +813,7 @@ def _write_global_variable_record(global_var: GlobalVariable, writer: _BitcodeWr
804813
writer.unabbrev_record(
805814
codes.MODULE_CODE_GLOBALVAR,
806815
*string_table[global_var.name.encode()], # STRTAB offset & size
807-
global_var.value.type.type_id,
816+
global_var.value_type.type_id,
808817
(global_var.address_space << 2) | 2 | global_var.is_constant,
809818
0 if global_var.initializer is None else global_var.initializer.id + 1,
810819
global_var.linkage._value_,

‎experimental/cuda-lang/src/cuda/lang/_passes/ir2llvm.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ def declare_function(self, name: str, func_ty: llvm.FunctionType) -> llvm.Value:
140140
self._function_declarations[name] = fptr
141141
return fptr
142142

143+
def unpack_struct(self, struct_value: llvm.Value) -> tuple[llvm.Value, ...]:
144+
assert isinstance(struct_value.type, llvm.AnonStructType)
145+
return tuple(self.builder.extract_value(struct_value, i)
146+
for i in range(len(struct_value.type.fields)))
147+
143148

144149
def generate_nvvm_bitcode_for_kernel(body: ir.Region,
145150
symbol: str,

‎experimental/cuda-lang/test/test_nvvm_bitcode.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ def _make_inline_ptx_bitcode() -> bytes:
8787
side_effects=False
8888
)
8989
r = builder.call(ptx_functy, ptx_func, [a, b])
90-
radd = builder.extract_value(tt.F32, r, 0)
91-
rsub = builder.extract_value(tt.F32, r, 1)
90+
radd = builder.extract_value(r, 0)
91+
rsub = builder.extract_value(r, 1)
9292
builder.store(cp0, radd, alignment=4)
9393
builder.store(cp1, rsub, alignment=4)
9494
builder.ret()

0 commit comments

Comments
 (0)