1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright 2014 Advanced Micro Devices, Inc.
3bf215546Sopenharmony_ci *
4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the
6bf215546Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
7bf215546Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
8bf215546Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
9bf215546Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
10bf215546Sopenharmony_ci * the following conditions:
11bf215546Sopenharmony_ci *
12bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
15bf215546Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
16bf215546Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
17bf215546Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
18bf215546Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE.
19bf215546Sopenharmony_ci *
20bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the
21bf215546Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
22bf215546Sopenharmony_ci * of the Software.
23bf215546Sopenharmony_ci *
24bf215546Sopenharmony_ci */
25bf215546Sopenharmony_ci
26bf215546Sopenharmony_ci#include <llvm-c/Core.h>
27bf215546Sopenharmony_ci#include <llvm/Analysis/TargetLibraryInfo.h>
28bf215546Sopenharmony_ci#include <llvm/IR/IRBuilder.h>
29bf215546Sopenharmony_ci#include <llvm/IR/LegacyPassManager.h>
30bf215546Sopenharmony_ci#include <llvm/Target/TargetMachine.h>
31bf215546Sopenharmony_ci#include <llvm/MC/MCSubtargetInfo.h>
32bf215546Sopenharmony_ci#include <llvm/Support/CommandLine.h>
33bf215546Sopenharmony_ci#include <llvm/Transforms/IPO.h>
34bf215546Sopenharmony_ci
35bf215546Sopenharmony_ci#include <cstring>
36bf215546Sopenharmony_ci
37bf215546Sopenharmony_ci/* DO NOT REORDER THE HEADERS
38bf215546Sopenharmony_ci * The LLVM headers need to all be included before any Mesa header,
39bf215546Sopenharmony_ci * as they use the `restrict` keyword in ways that are incompatible
40bf215546Sopenharmony_ci * with our #define in include/c99_compat.h
41bf215546Sopenharmony_ci */
42bf215546Sopenharmony_ci
43bf215546Sopenharmony_ci#include "ac_binary.h"
44bf215546Sopenharmony_ci#include "ac_llvm_util.h"
45bf215546Sopenharmony_ci#include "ac_llvm_build.h"
46bf215546Sopenharmony_ci#include "util/macros.h"
47bf215546Sopenharmony_ci
48bf215546Sopenharmony_cibool ac_is_llvm_processor_supported(LLVMTargetMachineRef tm, const char *processor)
49bf215546Sopenharmony_ci{
50bf215546Sopenharmony_ci   llvm::TargetMachine *TM = reinterpret_cast<llvm::TargetMachine *>(tm);
51bf215546Sopenharmony_ci   return TM->getMCSubtargetInfo()->isCPUStringValid(processor);
52bf215546Sopenharmony_ci}
53bf215546Sopenharmony_ci
54bf215546Sopenharmony_civoid ac_reset_llvm_all_options_occurences()
55bf215546Sopenharmony_ci{
56bf215546Sopenharmony_ci   llvm::cl::ResetAllOptionOccurrences();
57bf215546Sopenharmony_ci}
58bf215546Sopenharmony_ci
59bf215546Sopenharmony_civoid ac_add_attr_dereferenceable(LLVMValueRef val, uint64_t bytes)
60bf215546Sopenharmony_ci{
61bf215546Sopenharmony_ci   llvm::Argument *A = llvm::unwrap<llvm::Argument>(val);
62bf215546Sopenharmony_ci   A->addAttr(llvm::Attribute::getWithDereferenceableBytes(A->getContext(), bytes));
63bf215546Sopenharmony_ci}
64bf215546Sopenharmony_ci
65bf215546Sopenharmony_civoid ac_add_attr_alignment(LLVMValueRef val, uint64_t bytes)
66bf215546Sopenharmony_ci{
67bf215546Sopenharmony_ci   llvm::Argument *A = llvm::unwrap<llvm::Argument>(val);
68bf215546Sopenharmony_ci   A->addAttr(llvm::Attribute::getWithAlignment(A->getContext(), llvm::Align(bytes)));
69bf215546Sopenharmony_ci}
70bf215546Sopenharmony_ci
71bf215546Sopenharmony_cibool ac_is_sgpr_param(LLVMValueRef arg)
72bf215546Sopenharmony_ci{
73bf215546Sopenharmony_ci   llvm::Argument *A = llvm::unwrap<llvm::Argument>(arg);
74bf215546Sopenharmony_ci   llvm::AttributeList AS = A->getParent()->getAttributes();
75bf215546Sopenharmony_ci   unsigned ArgNo = A->getArgNo();
76bf215546Sopenharmony_ci   return AS.hasParamAttr(ArgNo, llvm::Attribute::InReg);
77bf215546Sopenharmony_ci}
78bf215546Sopenharmony_ci
79bf215546Sopenharmony_ciLLVMModuleRef ac_create_module(LLVMTargetMachineRef tm, LLVMContextRef ctx)
80bf215546Sopenharmony_ci{
81bf215546Sopenharmony_ci   llvm::TargetMachine *TM = reinterpret_cast<llvm::TargetMachine *>(tm);
82bf215546Sopenharmony_ci   LLVMModuleRef module = LLVMModuleCreateWithNameInContext("mesa-shader", ctx);
83bf215546Sopenharmony_ci
84bf215546Sopenharmony_ci   llvm::unwrap(module)->setTargetTriple(TM->getTargetTriple().getTriple());
85bf215546Sopenharmony_ci   llvm::unwrap(module)->setDataLayout(TM->createDataLayout());
86bf215546Sopenharmony_ci   return module;
87bf215546Sopenharmony_ci}
88bf215546Sopenharmony_ci
89bf215546Sopenharmony_ciLLVMBuilderRef ac_create_builder(LLVMContextRef ctx, enum ac_float_mode float_mode)
90bf215546Sopenharmony_ci{
91bf215546Sopenharmony_ci   LLVMBuilderRef builder = LLVMCreateBuilderInContext(ctx);
92bf215546Sopenharmony_ci
93bf215546Sopenharmony_ci   llvm::FastMathFlags flags;
94bf215546Sopenharmony_ci
95bf215546Sopenharmony_ci   switch (float_mode) {
96bf215546Sopenharmony_ci   case AC_FLOAT_MODE_DEFAULT:
97bf215546Sopenharmony_ci   case AC_FLOAT_MODE_DENORM_FLUSH_TO_ZERO:
98bf215546Sopenharmony_ci      break;
99bf215546Sopenharmony_ci
100bf215546Sopenharmony_ci   case AC_FLOAT_MODE_DEFAULT_OPENGL:
101bf215546Sopenharmony_ci      /* Allow optimizations to treat the sign of a zero argument or
102bf215546Sopenharmony_ci       * result as insignificant.
103bf215546Sopenharmony_ci       */
104bf215546Sopenharmony_ci      flags.setNoSignedZeros(); /* nsz */
105bf215546Sopenharmony_ci
106bf215546Sopenharmony_ci      /* Allow optimizations to use the reciprocal of an argument
107bf215546Sopenharmony_ci       * rather than perform division.
108bf215546Sopenharmony_ci       */
109bf215546Sopenharmony_ci      flags.setAllowReciprocal(); /* arcp */
110bf215546Sopenharmony_ci
111bf215546Sopenharmony_ci      llvm::unwrap(builder)->setFastMathFlags(flags);
112bf215546Sopenharmony_ci      break;
113bf215546Sopenharmony_ci   }
114bf215546Sopenharmony_ci
115bf215546Sopenharmony_ci   return builder;
116bf215546Sopenharmony_ci}
117bf215546Sopenharmony_ci
118bf215546Sopenharmony_civoid ac_enable_signed_zeros(struct ac_llvm_context *ctx)
119bf215546Sopenharmony_ci{
120bf215546Sopenharmony_ci   if (ctx->float_mode == AC_FLOAT_MODE_DEFAULT_OPENGL) {
121bf215546Sopenharmony_ci      auto *b = llvm::unwrap(ctx->builder);
122bf215546Sopenharmony_ci      llvm::FastMathFlags flags = b->getFastMathFlags();
123bf215546Sopenharmony_ci
124bf215546Sopenharmony_ci      /* This disables the optimization of (x + 0), which is used
125bf215546Sopenharmony_ci       * to convert negative zero to positive zero.
126bf215546Sopenharmony_ci       */
127bf215546Sopenharmony_ci      flags.setNoSignedZeros(false);
128bf215546Sopenharmony_ci      b->setFastMathFlags(flags);
129bf215546Sopenharmony_ci   }
130bf215546Sopenharmony_ci}
131bf215546Sopenharmony_ci
132bf215546Sopenharmony_civoid ac_disable_signed_zeros(struct ac_llvm_context *ctx)
133bf215546Sopenharmony_ci{
134bf215546Sopenharmony_ci   if (ctx->float_mode == AC_FLOAT_MODE_DEFAULT_OPENGL) {
135bf215546Sopenharmony_ci      auto *b = llvm::unwrap(ctx->builder);
136bf215546Sopenharmony_ci      llvm::FastMathFlags flags = b->getFastMathFlags();
137bf215546Sopenharmony_ci
138bf215546Sopenharmony_ci      flags.setNoSignedZeros();
139bf215546Sopenharmony_ci      b->setFastMathFlags(flags);
140bf215546Sopenharmony_ci   }
141bf215546Sopenharmony_ci}
142bf215546Sopenharmony_ci
143bf215546Sopenharmony_ciLLVMTargetLibraryInfoRef ac_create_target_library_info(const char *triple)
144bf215546Sopenharmony_ci{
145bf215546Sopenharmony_ci   return reinterpret_cast<LLVMTargetLibraryInfoRef>(
146bf215546Sopenharmony_ci      new llvm::TargetLibraryInfoImpl(llvm::Triple(triple)));
147bf215546Sopenharmony_ci}
148bf215546Sopenharmony_ci
149bf215546Sopenharmony_civoid ac_dispose_target_library_info(LLVMTargetLibraryInfoRef library_info)
150bf215546Sopenharmony_ci{
151bf215546Sopenharmony_ci   delete reinterpret_cast<llvm::TargetLibraryInfoImpl *>(library_info);
152bf215546Sopenharmony_ci}
153bf215546Sopenharmony_ci
154bf215546Sopenharmony_ci/* Implementation of raw_pwrite_stream that works on malloc()ed memory for
155bf215546Sopenharmony_ci * better compatibility with C code. */
156bf215546Sopenharmony_cistruct raw_memory_ostream : public llvm::raw_pwrite_stream {
157bf215546Sopenharmony_ci   char *buffer;
158bf215546Sopenharmony_ci   size_t written;
159bf215546Sopenharmony_ci   size_t bufsize;
160bf215546Sopenharmony_ci
161bf215546Sopenharmony_ci   raw_memory_ostream()
162bf215546Sopenharmony_ci   {
163bf215546Sopenharmony_ci      buffer = NULL;
164bf215546Sopenharmony_ci      written = 0;
165bf215546Sopenharmony_ci      bufsize = 0;
166bf215546Sopenharmony_ci      SetUnbuffered();
167bf215546Sopenharmony_ci   }
168bf215546Sopenharmony_ci
169bf215546Sopenharmony_ci   ~raw_memory_ostream()
170bf215546Sopenharmony_ci   {
171bf215546Sopenharmony_ci      free(buffer);
172bf215546Sopenharmony_ci   }
173bf215546Sopenharmony_ci
174bf215546Sopenharmony_ci   void clear()
175bf215546Sopenharmony_ci   {
176bf215546Sopenharmony_ci      written = 0;
177bf215546Sopenharmony_ci   }
178bf215546Sopenharmony_ci
179bf215546Sopenharmony_ci   void take(char *&out_buffer, size_t &out_size)
180bf215546Sopenharmony_ci   {
181bf215546Sopenharmony_ci      out_buffer = buffer;
182bf215546Sopenharmony_ci      out_size = written;
183bf215546Sopenharmony_ci      buffer = NULL;
184bf215546Sopenharmony_ci      written = 0;
185bf215546Sopenharmony_ci      bufsize = 0;
186bf215546Sopenharmony_ci   }
187bf215546Sopenharmony_ci
188bf215546Sopenharmony_ci   void flush() = delete;
189bf215546Sopenharmony_ci
190bf215546Sopenharmony_ci   void write_impl(const char *ptr, size_t size) override
191bf215546Sopenharmony_ci   {
192bf215546Sopenharmony_ci      if (unlikely(written + size < written))
193bf215546Sopenharmony_ci         abort();
194bf215546Sopenharmony_ci      if (written + size > bufsize) {
195bf215546Sopenharmony_ci         bufsize = MAX3(1024, written + size, bufsize / 3 * 4);
196bf215546Sopenharmony_ci         buffer = (char *)realloc(buffer, bufsize);
197bf215546Sopenharmony_ci         if (!buffer) {
198bf215546Sopenharmony_ci            fprintf(stderr, "amd: out of memory allocating ELF buffer\n");
199bf215546Sopenharmony_ci            abort();
200bf215546Sopenharmony_ci         }
201bf215546Sopenharmony_ci      }
202bf215546Sopenharmony_ci      memcpy(buffer + written, ptr, size);
203bf215546Sopenharmony_ci      written += size;
204bf215546Sopenharmony_ci   }
205bf215546Sopenharmony_ci
206bf215546Sopenharmony_ci   void pwrite_impl(const char *ptr, size_t size, uint64_t offset) override
207bf215546Sopenharmony_ci   {
208bf215546Sopenharmony_ci      assert(offset == (size_t)offset && offset + size >= offset && offset + size <= written);
209bf215546Sopenharmony_ci      memcpy(buffer + offset, ptr, size);
210bf215546Sopenharmony_ci   }
211bf215546Sopenharmony_ci
212bf215546Sopenharmony_ci   uint64_t current_pos() const override
213bf215546Sopenharmony_ci   {
214bf215546Sopenharmony_ci      return written;
215bf215546Sopenharmony_ci   }
216bf215546Sopenharmony_ci};
217bf215546Sopenharmony_ci
218bf215546Sopenharmony_ci/* The LLVM compiler is represented as a pass manager containing passes for
219bf215546Sopenharmony_ci * optimizations, instruction selection, and code generation.
220bf215546Sopenharmony_ci */
221bf215546Sopenharmony_cistruct ac_compiler_passes {
222bf215546Sopenharmony_ci   raw_memory_ostream ostream;        /* ELF shader binary stream */
223bf215546Sopenharmony_ci   llvm::legacy::PassManager passmgr; /* list of passes */
224bf215546Sopenharmony_ci};
225bf215546Sopenharmony_ci
226bf215546Sopenharmony_cistruct ac_compiler_passes *ac_create_llvm_passes(LLVMTargetMachineRef tm)
227bf215546Sopenharmony_ci{
228bf215546Sopenharmony_ci   struct ac_compiler_passes *p = new ac_compiler_passes();
229bf215546Sopenharmony_ci   if (!p)
230bf215546Sopenharmony_ci      return NULL;
231bf215546Sopenharmony_ci
232bf215546Sopenharmony_ci   llvm::TargetMachine *TM = reinterpret_cast<llvm::TargetMachine *>(tm);
233bf215546Sopenharmony_ci
234bf215546Sopenharmony_ci   if (TM->addPassesToEmitFile(p->passmgr, p->ostream, nullptr,
235bf215546Sopenharmony_ci                               llvm::CGFT_ObjectFile)) {
236bf215546Sopenharmony_ci      fprintf(stderr, "amd: TargetMachine can't emit a file of this type!\n");
237bf215546Sopenharmony_ci      delete p;
238bf215546Sopenharmony_ci      return NULL;
239bf215546Sopenharmony_ci   }
240bf215546Sopenharmony_ci   return p;
241bf215546Sopenharmony_ci}
242bf215546Sopenharmony_ci
243bf215546Sopenharmony_civoid ac_destroy_llvm_passes(struct ac_compiler_passes *p)
244bf215546Sopenharmony_ci{
245bf215546Sopenharmony_ci   delete p;
246bf215546Sopenharmony_ci}
247bf215546Sopenharmony_ci
248bf215546Sopenharmony_ci/* This returns false on failure. */
249bf215546Sopenharmony_cibool ac_compile_module_to_elf(struct ac_compiler_passes *p, LLVMModuleRef module,
250bf215546Sopenharmony_ci                              char **pelf_buffer, size_t *pelf_size)
251bf215546Sopenharmony_ci{
252bf215546Sopenharmony_ci   p->passmgr.run(*llvm::unwrap(module));
253bf215546Sopenharmony_ci   p->ostream.take(*pelf_buffer, *pelf_size);
254bf215546Sopenharmony_ci   return true;
255bf215546Sopenharmony_ci}
256bf215546Sopenharmony_ci
257bf215546Sopenharmony_civoid ac_llvm_add_barrier_noop_pass(LLVMPassManagerRef passmgr)
258bf215546Sopenharmony_ci{
259bf215546Sopenharmony_ci   llvm::unwrap(passmgr)->add(llvm::createBarrierNoopPass());
260bf215546Sopenharmony_ci}
261bf215546Sopenharmony_ci
262bf215546Sopenharmony_ciLLVMValueRef ac_build_atomic_rmw(struct ac_llvm_context *ctx, LLVMAtomicRMWBinOp op,
263bf215546Sopenharmony_ci                                 LLVMValueRef ptr, LLVMValueRef val, const char *sync_scope)
264bf215546Sopenharmony_ci{
265bf215546Sopenharmony_ci   llvm::AtomicRMWInst::BinOp binop;
266bf215546Sopenharmony_ci   switch (op) {
267bf215546Sopenharmony_ci   case LLVMAtomicRMWBinOpXchg:
268bf215546Sopenharmony_ci      binop = llvm::AtomicRMWInst::Xchg;
269bf215546Sopenharmony_ci      break;
270bf215546Sopenharmony_ci   case LLVMAtomicRMWBinOpAdd:
271bf215546Sopenharmony_ci      binop = llvm::AtomicRMWInst::Add;
272bf215546Sopenharmony_ci      break;
273bf215546Sopenharmony_ci   case LLVMAtomicRMWBinOpSub:
274bf215546Sopenharmony_ci      binop = llvm::AtomicRMWInst::Sub;
275bf215546Sopenharmony_ci      break;
276bf215546Sopenharmony_ci   case LLVMAtomicRMWBinOpAnd:
277bf215546Sopenharmony_ci      binop = llvm::AtomicRMWInst::And;
278bf215546Sopenharmony_ci      break;
279bf215546Sopenharmony_ci   case LLVMAtomicRMWBinOpNand:
280bf215546Sopenharmony_ci      binop = llvm::AtomicRMWInst::Nand;
281bf215546Sopenharmony_ci      break;
282bf215546Sopenharmony_ci   case LLVMAtomicRMWBinOpOr:
283bf215546Sopenharmony_ci      binop = llvm::AtomicRMWInst::Or;
284bf215546Sopenharmony_ci      break;
285bf215546Sopenharmony_ci   case LLVMAtomicRMWBinOpXor:
286bf215546Sopenharmony_ci      binop = llvm::AtomicRMWInst::Xor;
287bf215546Sopenharmony_ci      break;
288bf215546Sopenharmony_ci   case LLVMAtomicRMWBinOpMax:
289bf215546Sopenharmony_ci      binop = llvm::AtomicRMWInst::Max;
290bf215546Sopenharmony_ci      break;
291bf215546Sopenharmony_ci   case LLVMAtomicRMWBinOpMin:
292bf215546Sopenharmony_ci      binop = llvm::AtomicRMWInst::Min;
293bf215546Sopenharmony_ci      break;
294bf215546Sopenharmony_ci   case LLVMAtomicRMWBinOpUMax:
295bf215546Sopenharmony_ci      binop = llvm::AtomicRMWInst::UMax;
296bf215546Sopenharmony_ci      break;
297bf215546Sopenharmony_ci   case LLVMAtomicRMWBinOpUMin:
298bf215546Sopenharmony_ci      binop = llvm::AtomicRMWInst::UMin;
299bf215546Sopenharmony_ci      break;
300bf215546Sopenharmony_ci   case LLVMAtomicRMWBinOpFAdd:
301bf215546Sopenharmony_ci      binop = llvm::AtomicRMWInst::FAdd;
302bf215546Sopenharmony_ci      break;
303bf215546Sopenharmony_ci   default:
304bf215546Sopenharmony_ci      unreachable("invalid LLVMAtomicRMWBinOp");
305bf215546Sopenharmony_ci      break;
306bf215546Sopenharmony_ci   }
307bf215546Sopenharmony_ci   unsigned SSID = llvm::unwrap(ctx->context)->getOrInsertSyncScopeID(sync_scope);
308bf215546Sopenharmony_ci   return llvm::wrap(llvm::unwrap(ctx->builder)
309bf215546Sopenharmony_ci                        ->CreateAtomicRMW(binop, llvm::unwrap(ptr), llvm::unwrap(val),
310bf215546Sopenharmony_ci#if LLVM_VERSION_MAJOR >= 13
311bf215546Sopenharmony_ci                                          llvm::MaybeAlign(0),
312bf215546Sopenharmony_ci#endif
313bf215546Sopenharmony_ci                                          llvm::AtomicOrdering::SequentiallyConsistent, SSID));
314bf215546Sopenharmony_ci}
315bf215546Sopenharmony_ci
316bf215546Sopenharmony_ciLLVMValueRef ac_build_atomic_cmp_xchg(struct ac_llvm_context *ctx, LLVMValueRef ptr,
317bf215546Sopenharmony_ci                                      LLVMValueRef cmp, LLVMValueRef val, const char *sync_scope)
318bf215546Sopenharmony_ci{
319bf215546Sopenharmony_ci   unsigned SSID = llvm::unwrap(ctx->context)->getOrInsertSyncScopeID(sync_scope);
320bf215546Sopenharmony_ci   return llvm::wrap(llvm::unwrap(ctx->builder)
321bf215546Sopenharmony_ci                        ->CreateAtomicCmpXchg(llvm::unwrap(ptr), llvm::unwrap(cmp),
322bf215546Sopenharmony_ci                                              llvm::unwrap(val),
323bf215546Sopenharmony_ci#if LLVM_VERSION_MAJOR >= 13
324bf215546Sopenharmony_ci                                              llvm::MaybeAlign(0),
325bf215546Sopenharmony_ci#endif
326bf215546Sopenharmony_ci                                              llvm::AtomicOrdering::SequentiallyConsistent,
327bf215546Sopenharmony_ci                                              llvm::AtomicOrdering::SequentiallyConsistent, SSID));
328bf215546Sopenharmony_ci}
329