1 module d.llvm.intrinsic;
2 
3 import d.llvm.local;
4 
5 import source.name;
6 
7 import d.ir.expression;
8 
9 import llvm.c.core;
10 
11 struct IntrinsicGenData {
12 private:
13 	LLVMValueRef[Name] cache;
14 }
15 
16 struct IntrinsicGen {
17 	private LocalPass pass;
18 	alias pass this;
19 	
20 	this(LocalPass pass) {
21 		this.pass = pass;
22 	}
23 	
24 	private @property
25 	ref LLVMValueRef[Name] cache() {
26 		return intrinsicGenData.cache;
27 	}
28 	
29 	LLVMValueRef build(Intrinsic i, Expression[] args) {
30 		import d.llvm.expression, std.algorithm, std.range;
31 		return build(i, args.map!(a => ExpressionGen(pass).visit(a)).array());
32 	}
33 	
34 	LLVMValueRef build(Intrinsic i, LLVMValueRef[] args) {
35 		bool weak;
36 		final switch(i) with(Intrinsic) {
37 			case None:
38 				assert(0, "invalid intrinsic");
39 			
40 			case Expect:
41 				return expect(args);
42 			
43 			case CompareAndSwap:
44 				return cas(weak, args);
45 			
46 			case CompareAndSwapWeak:
47 				weak = true;
48 				goto case CompareAndSwap;
49 			
50 			case PopCount:
51 				return ctpop(args);
52 			
53 			case CountLeadingZeros:
54 				return ctlz(args);
55 			
56 			case CountTrailingZeros:
57 				return cttz(args);
58 			
59 			case ByteSwap:
60 				return bswap(args);
61 		}
62 	}
63 	
64 	LLVMValueRef expect(LLVMValueRef[] args) in {
65 		assert(args.length == 2, "Invalid argument count");
66 	} do {
67 		return expect(args[0], args[1]);
68 	}
69 	
70 	LLVMValueRef expect(LLVMValueRef v, LLVMValueRef e) {
71 		LLVMValueRef[2] args = [v, e];
72 		return LLVMBuildCall(builder, getExpect(), args.ptr, args.length, "");
73 	}
74 	
75 	auto getExpect() {
76 		auto name = context.getName("llvm.expect.i1");
77 		if (auto fPtr = name in cache) {
78 			return *fPtr;
79 		}
80 		
81 		auto i1 = LLVMInt1TypeInContext(llvmCtx);
82 		LLVMTypeRef[2] params = [i1, i1];
83 		
84 		auto type = LLVMFunctionType(i1, params.ptr, params.length, false);
85 		return cache[name] = LLVMAddFunction(
86 			dmodule,
87 			name.toStringz(context),
88 			type,
89 		);
90 	}
91 	
92 	LLVMValueRef cas(bool weak, LLVMValueRef[] args) in {
93 		assert(args.length == 3, "Invalid argument count");
94 	} do {
95 		return cas(
96 			weak,
97 			args[0],
98 			args[1],
99 			args[2],
100 			LLVMAtomicOrdering.SequentiallyConsistent,
101 		);
102 	}
103 	
104 	LLVMValueRef cas(
105 		bool weak,
106 		LLVMValueRef ptr,
107 		LLVMValueRef old,
108 		LLVMValueRef val,
109 		LLVMAtomicOrdering ordering,
110 	) {
111 		return LLVMBuildAtomicCmpXchg(
112 			builder,
113 			ptr,
114 			old,
115 			val,
116 			ordering,
117 			ordering,
118 			false,
119 		);
120 	}
121 	
122 	LLVMValueRef ctpop(LLVMValueRef[] args) in {
123 		assert(args.length == 1, "Invalid argument count");
124 	} do {
125 		return ctpop(args[0]);
126 	}
127 	
128 	LLVMValueRef ctpop(LLVMValueRef n) {
129 		auto bits = LLVMGetIntTypeWidth(LLVMTypeOf(n));
130 		return LLVMBuildCall(builder, getCtpop(bits), &n, 1, "");
131 	}
132 	
133 	auto getCtpop(uint bits) {
134 		import std.conv;
135 		auto name = context.getName("llvm.ctpop.i" ~ to!string(bits));
136 		if (auto fPtr = name in cache) {
137 			return *fPtr;
138 		}
139 		
140 		auto t = LLVMIntTypeInContext(llvmCtx, bits);
141 		return cache[name] = LLVMAddFunction(
142 			dmodule,
143 			name.toStringz(context),
144 			LLVMFunctionType(t, &t, 1, false),
145 		);
146 	}
147 	
148 	LLVMValueRef ctlz(LLVMValueRef[] args) in {
149 		assert(args.length == 1, "Invalid argument count");
150 	} do {
151 		return ctlz(args[0]);
152 	}
153 	
154 	LLVMValueRef ctlz(LLVMValueRef n) {
155 		LLVMValueRef[2] args = [
156 			n,
157 			LLVMConstInt(LLVMInt1TypeInContext(llvmCtx), false, false),
158 		];
159 		
160 		auto bits = LLVMGetIntTypeWidth(LLVMTypeOf(n));
161 		return LLVMBuildCall(builder, getCtlz(bits), args.ptr, args.length, "");
162 	}
163 	
164 	auto getCtlz(uint bits) {
165 		import std.conv;
166 		auto name = context.getName("llvm.ctlz.i" ~ to!string(bits));
167 		if (auto fPtr = name in cache) {
168 			return *fPtr;
169 		}
170 		
171 		auto t = LLVMIntTypeInContext(llvmCtx, bits);
172 		LLVMTypeRef[2] params = [t, LLVMInt1TypeInContext(llvmCtx)];
173 		
174 		auto type = LLVMFunctionType(t, params.ptr, params.length, false);
175 		return cache[name] = LLVMAddFunction(
176 			dmodule,
177 			name.toStringz(context),
178 			type,
179 		);
180 	}
181 	
182 	LLVMValueRef cttz(LLVMValueRef[] args) in {
183 		assert(args.length == 1, "Invalid argument count");
184 	} do {
185 		return cttz(args[0]);
186 	}
187 	
188 	LLVMValueRef cttz(LLVMValueRef n) {
189 		LLVMValueRef[2] args = [
190 			n,
191 			LLVMConstInt(LLVMInt1TypeInContext(llvmCtx), false, false),
192 		];
193 		
194 		auto bits = LLVMGetIntTypeWidth(LLVMTypeOf(n));
195 		return LLVMBuildCall(builder, getCttz(bits), args.ptr, args.length, "");
196 	}
197 	
198 	auto getCttz(uint bits) {
199 		import std.conv;
200 		auto name = context.getName("llvm.cttz.i" ~ to!string(bits));
201 		if (auto fPtr = name in cache) {
202 			return *fPtr;
203 		}
204 		
205 		auto t = LLVMIntTypeInContext(llvmCtx, bits);
206 		LLVMTypeRef[2] params = [t, LLVMInt1TypeInContext(llvmCtx)];
207 		
208 		auto type = LLVMFunctionType(t, params.ptr, params.length, false);
209 		return cache[name] = LLVMAddFunction(
210 			dmodule,
211 			name.toStringz(context),
212 			type,
213 		);
214 	}
215 
216 	LLVMValueRef bswap(LLVMValueRef[] args) in {
217 		assert(args.length == 1, "Invalid argument count");
218 	} do {
219 		return bswap(args[0]);
220 	}
221 	
222 	LLVMValueRef bswap(LLVMValueRef n) {
223 		auto bits = LLVMGetIntTypeWidth(LLVMTypeOf(n));
224 		return LLVMBuildCall(builder, getBswap(bits), &n, 1, "");
225 	}
226 	
227 	auto getBswap(uint bits) {
228 		import std.conv;
229 		auto name = context.getName("llvm.bswap.i" ~ to!string(bits));
230 		if (auto fPtr = name in cache) {
231 			return *fPtr;
232 		}
233 		
234 		auto t = LLVMIntTypeInContext(llvmCtx, bits);
235 		return cache[name] = LLVMAddFunction(
236 			dmodule,
237 			name.toStringz(context),
238 			LLVMFunctionType(t, &t, 1, false),
239 		);
240 	}
241 }