Enzyme main
Loading...
Searching...
No Matches
RaiseLLVMExtPass.cpp
Go to the documentation of this file.
1//===- RaiseLLVMExtPass.cpp - Raise LLVM Ext operations ------------------ //
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements a pass to raise LLVM ops to the LLVM Ext
10// dialect.
11//
12//===----------------------------------------------------------------------===//
13
15#include "Passes/Passes.h"
16
17#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
18
19#include "mlir/IR/Matchers.h"
20#include "mlir/IR/PatternMatch.h"
21#include "mlir/IR/SymbolTable.h"
22
23namespace mlir {
24namespace enzyme {
25using namespace mlir::enzyme;
26#define GEN_PASS_DEF_RAISELLVMEXTPASS
27#include "Passes/Passes.h.inc"
28} // namespace enzyme
29} // namespace mlir
30
31namespace {
32using namespace mlir;
33using namespace enzyme;
34
35struct RaiseLLVMExtPass
36 : public enzyme::impl::RaiseLLVMExtPassBase<RaiseLLVMExtPass> {
37 using RaiseLLVMExtPassBase::RaiseLLVMExtPassBase;
38
39 void runOnOperation() override {
40 bool failed = false;
41
42 SymbolTable::walkSymbolTables(
43 getOperation(),
44 /*allUsesVisible*/ true, [&](Operation *st, bool allUsesVisible) {
45 SymbolTable symtable(st);
46
47 auto name = StringAttr::get(&getContext(), "__enzyme_ptr_size_hint");
48 auto uses = SymbolTable::getSymbolUses(name, st);
49
50 // getSymbolUses returns an empty (not nullopt) range when the
51 // symbol simply isn't referenced anywhere in this module, which is
52 // the common case (most translation units never call
53 // __enzyme_ptr_size_hint); only bail out on the lookup+cast below
54 // if there's actually a use to process, since symtable.lookup(name)
55 // returns null when the symbol isn't declared in this module at
56 // all, and casting that to FunctionOpInterface crashes.
57 if (!uses || uses->empty())
58 return;
59
60 auto fn = cast<FunctionOpInterface>(symtable.lookup(name));
61 if (!fn.isExternal()) {
62 failed = true;
63 fn.emitError() << "__enzyme_ptr_size_hint is not declared external";
64 return;
65 }
66
67 for (auto use : *uses) {
68 auto call = dyn_cast<LLVM::CallOp>(use.getUser());
69 if (!call) {
70 failed = true;
71 use.getUser()->emitError()
72 << "user of __enzyme_ptr_size_hint is not a llvm.call";
73 return;
74 }
75
76 auto args = call.getArgOperands();
77 if (args.size() < 2 || args.size() > 3) {
78 failed = true;
79 call.emitError() << "__enzyme_ptr_size_hint expects (ptr, size) "
80 "or (ptr, size, addrspace), got "
81 << args.size() << " arguments";
82 return;
83 }
84
85 auto ptrTy = dyn_cast<LLVM::LLVMPointerType>(args[0].getType());
86 if (!ptrTy) {
87 failed = true;
88 call.emitError()
89 << "first argument of __enzyme_ptr_size_hint is not a "
90 "pointer";
91 return;
92 }
93
94 OpBuilder builder(call);
95 Value ptr = args[0];
96
97 // The optional third argument names the memory space the pointer
98 // really addresses. The callers that need it are the ones whose
99 // frontend cannot put that space in the type -- a cudaMalloc'd
100 // buffer is a plain `float *`, address space 0 like every other
101 // host pointer in CUDA C -- so rather than rejecting an annotation
102 // that disagrees with the type, hint an llvm.addrspacecast to the
103 // annotated space. Everything downstream keeps taking the memory
104 // space from a pointer's own type: the clone of this allocation is
105 // made from the cast, so it is allocated and copied with the device
106 // runtime instead of malloc/memcpy.
107 if (args.size() == 3) {
108 APInt space;
109 if (!matchPattern(args[2], m_ConstantInt(&space))) {
110 failed = true;
111 call.emitError() << "address space argument of "
112 "__enzyme_ptr_size_hint is not a constant";
113 return;
114 }
115 if (space.isNegative()) {
116 failed = true;
117 call.emitError() << "address space argument of "
118 "__enzyme_ptr_size_hint is negative: "
119 << space.getSExtValue();
120 return;
121 }
122 if (space.getZExtValue() != ptrTy.getAddressSpace()) {
123 auto castTy = LLVM::LLVMPointerType::get(
124 &getContext(), (unsigned)space.getZExtValue());
125 ptr = LLVM::AddrSpaceCastOp::create(builder, call.getLoc(),
126 castTy, ptr);
127 }
128 }
129
130 llvm_ext::PtrSizeHintOp::create(builder, call.getLoc(), ptr,
131 args[1]);
132
133 call.erase();
134 }
135
136 symtable.erase(fn);
137 });
138
139 if (failed)
140 signalPassFailure();
141 }
142};
143
144} // end anonymous namespace