radeon/llvm: Free libelf resources
[mesa.git] / src / gallium / drivers / radeon / radeon_llvm_emit.c
1 /*
2 * Copyright 2011 Advanced Micro Devices, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * Authors: Tom Stellard <thomas.stellard@amd.com>
24 *
25 */
26 #include "radeon_llvm_emit.h"
27 #include "util/u_memory.h"
28
29 #include <llvm-c/Target.h>
30 #include <llvm-c/TargetMachine.h>
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <libelf.h>
36 #include <gelf.h>
37
38 #define CPU_STRING_LEN 30
39 #define FS_STRING_LEN 30
40 #define TRIPLE_STRING_LEN 7
41
42 /**
43 * Set the shader type we want to compile
44 *
45 * @param type shader type to set
46 */
47 void radeon_llvm_shader_type(LLVMValueRef F, unsigned type)
48 {
49 char Str[2];
50 sprintf(Str, "%1d", type);
51
52 LLVMAddTargetDependentFunctionAttr(F, "ShaderType", Str);
53 }
54
55 static void init_r600_target() {
56 static unsigned initialized = 0;
57 if (!initialized) {
58 LLVMInitializeR600TargetInfo();
59 LLVMInitializeR600Target();
60 LLVMInitializeR600TargetMC();
61 LLVMInitializeR600AsmPrinter();
62 initialized = 1;
63 }
64 }
65
66 static LLVMTargetRef get_r600_target() {
67 LLVMTargetRef target = NULL;
68
69 for (target = LLVMGetFirstTarget(); target;
70 target = LLVMGetNextTarget(target)) {
71 if (!strncmp(LLVMGetTargetName(target), "r600", 4)) {
72 break;
73 }
74 }
75
76 if (!target) {
77 fprintf(stderr, "Can't find target r600\n");
78 return NULL;
79 }
80 return target;
81 }
82
83 /**
84 * Compile an LLVM module to machine code.
85 *
86 * @returns 0 for success, 1 for failure
87 */
88 unsigned radeon_llvm_compile(LLVMModuleRef M, struct radeon_llvm_binary *binary,
89 const char * gpu_family, unsigned dump) {
90
91 LLVMTargetRef target;
92 LLVMTargetMachineRef tm;
93 char cpu[CPU_STRING_LEN];
94 char fs[FS_STRING_LEN];
95 char *err;
96 LLVMMemoryBufferRef out_buffer;
97 unsigned buffer_size;
98 const char *buffer_data;
99 char triple[TRIPLE_STRING_LEN];
100 char *elf_buffer;
101 Elf *elf;
102 Elf_Scn *section = NULL;
103 size_t section_str_index;
104 LLVMBool r;
105
106 init_r600_target();
107
108 target = get_r600_target();
109 if (!target) {
110 return 1;
111 }
112
113 strncpy(cpu, gpu_family, CPU_STRING_LEN);
114 memset(fs, 0, sizeof(fs));
115 if (dump) {
116 LLVMDumpModule(M);
117 strncpy(fs, "+DumpCode", FS_STRING_LEN);
118 }
119 strncpy(triple, "r600--", TRIPLE_STRING_LEN);
120 tm = LLVMCreateTargetMachine(target, triple, cpu, fs,
121 LLVMCodeGenLevelDefault, LLVMRelocDefault,
122 LLVMCodeModelDefault);
123
124 r = LLVMTargetMachineEmitToMemoryBuffer(tm, M, LLVMObjectFile, &err,
125 &out_buffer);
126 if (r) {
127 fprintf(stderr, "%s", err);
128 FREE(err);
129 return 1;
130 }
131
132 buffer_size = LLVMGetBufferSize(out_buffer);
133 buffer_data = LLVMGetBufferStart(out_buffer);
134
135 /* One of the libelf implementations
136 * (http://www.mr511.de/software/english.htm) requires calling
137 * elf_version() before elf_memory().
138 */
139 elf_version(EV_CURRENT);
140 elf_buffer = MALLOC(buffer_size);
141 memcpy(elf_buffer, buffer_data, buffer_size);
142
143 elf = elf_memory(elf_buffer, buffer_size);
144
145 elf_getshdrstrndx(elf, &section_str_index);
146 binary->disassembled = 0;
147
148 while ((section = elf_nextscn(elf, section))) {
149 const char *name;
150 Elf_Data *section_data = NULL;
151 GElf_Shdr section_header;
152 if (gelf_getshdr(section, &section_header) != &section_header) {
153 fprintf(stderr, "Failed to read ELF section header\n");
154 return 1;
155 }
156 name = elf_strptr(elf, section_str_index, section_header.sh_name);
157 if (!strcmp(name, ".text")) {
158 section_data = elf_getdata(section, section_data);
159 binary->code_size = section_data->d_size;
160 binary->code = MALLOC(binary->code_size * sizeof(unsigned char));
161 memcpy(binary->code, section_data->d_buf, binary->code_size);
162 } else if (!strcmp(name, ".AMDGPU.config")) {
163 section_data = elf_getdata(section, section_data);
164 binary->config_size = section_data->d_size;
165 binary->config = MALLOC(binary->config_size * sizeof(unsigned char));
166 memcpy(binary->config, section_data->d_buf, binary->config_size);
167 } else if (dump && !strcmp(name, ".AMDGPU.disasm")) {
168 binary->disassembled = 1;
169 section_data = elf_getdata(section, section_data);
170 fprintf(stderr, "\nShader Disassembly:\n\n");
171 fprintf(stderr, "%.*s\n", (int)section_data->d_size,
172 (char *)section_data->d_buf);
173 }
174 }
175
176 if (elf){
177 elf_end(elf);
178 }
179 LLVMDisposeMemoryBuffer(out_buffer);
180 LLVMDisposeTargetMachine(tm);
181 return 0;
182 }