Skip to content

Commit d629302

Browse files
committed
WIP: Add support for machine-dependent builtins
1 parent 0873358 commit d629302

File tree

10 files changed

+251
-26
lines changed

10 files changed

+251
-26
lines changed

Cargo.lock

Lines changed: 0 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ path = "tests/lib.rs"
1414
harness = false
1515

1616
[dependencies]
17-
gccjit = { git = "https://github.com/antoyo/gccjit.rs", branch = "feature/rustc" }
17+
#gccjit = { git = "https://github.com/antoyo/gccjit.rs", branch = "feature/rustc" }
18+
gccjit = { path = "/home/bouanto/Ordinateur/Programmation/Rust/Projets/gccjit.rs" }
1819

1920
target-lexicon = "0.10.0"
2021

gcc-test-backend/src/main.rs

Lines changed: 173 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,175 @@
1+
#![feature(link_llvm_intrinsics)]
2+
3+
use core::arch::x86_64::__m128i;
4+
5+
unsafe fn _mm_movemask_epi8(a: i128) -> i32 {
6+
pmovmskb(a)
7+
}
8+
9+
#[allow(improper_ctypes)]
10+
extern "C" {
11+
#[link_name = "llvm.x86.sse2.pmovmskb.128"]
12+
fn pmovmskb(a: i128) -> i32;
13+
}
14+
15+
fn main() {
16+
unsafe {
17+
_mm_movemask_epi8(12);
18+
}
19+
}
20+
21+
/*#![feature(core_intrinsics, generators, generator_trait, is_sorted)]
22+
23+
use std::arch::x86_64::*;
24+
125
fn main() {
2-
println!("{}", 1010_usize.count_ones());
26+
unsafe {
27+
test_simd();
28+
}
29+
}
30+
31+
#[target_feature(enable = "sse2")]
32+
unsafe fn test_simd() {
33+
/*let x = _mm_setzero_si128();
34+
let y = _mm_set1_epi16(7);
35+
let or = _mm_or_si128(x, y);
36+
let cmp_eq = _mm_cmpeq_epi8(y, y);
37+
let cmp_lt = _mm_cmplt_epi8(y, y);*/
38+
39+
/*assert_eq!(std::mem::transmute::<_, [u16; 8]>(or), [7, 7, 7, 7, 7, 7, 7, 7]);
40+
assert_eq!(std::mem::transmute::<_, [u16; 8]>(cmp_eq), [0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff]);
41+
assert_eq!(std::mem::transmute::<_, [u16; 8]>(cmp_lt), [0, 0, 0, 0, 0, 0, 0, 0]);*/
42+
43+
/*test_mm_slli_si128();
44+
test_mm_movemask_epi8();
45+
test_mm256_movemask_epi8();*/
46+
test_mm_add_epi8();
47+
test_mm_add_pd();
48+
/*test_mm_cvtepi8_epi16();
49+
test_mm_cvtsi128_si64();
50+
51+
// FIXME(#666) implement `#[rustc_arg_required_const(..)]` support
52+
//test_mm_extract_epi8();
53+
54+
let mask1 = _mm_movemask_epi8(dbg!(_mm_setr_epi8(255u8 as i8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)));
55+
assert_eq!(mask1, 1);*/
56+
}
57+
58+
#[target_feature(enable = "sse2")]
59+
unsafe fn test_mm_slli_si128() {
60+
let a = _mm_setr_epi8(
61+
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
62+
);
63+
let r = _mm_slli_si128(a, 1);
64+
let e = _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
65+
assert_eq_m128i(r, e);
66+
67+
let a = _mm_setr_epi8(
68+
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
69+
);
70+
let r = _mm_slli_si128(a, 15);
71+
let e = _mm_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
72+
assert_eq_m128i(r, e);
73+
74+
let a = _mm_setr_epi8(
75+
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
76+
);
77+
let r = _mm_slli_si128(a, 16);
78+
assert_eq_m128i(r, _mm_set1_epi8(0));
79+
80+
let a = _mm_setr_epi8(
81+
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
82+
);
83+
let r = _mm_slli_si128(a, -1);
84+
assert_eq_m128i(_mm_set1_epi8(0), r);
85+
86+
let a = _mm_setr_epi8(
87+
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
88+
);
89+
let r = _mm_slli_si128(a, -0x80000000);
90+
assert_eq_m128i(r, _mm_set1_epi8(0));
91+
}
92+
93+
#[target_feature(enable = "sse2")]
94+
unsafe fn test_mm_movemask_epi8() {
95+
let a = _mm_setr_epi8(
96+
0b1000_0000u8 as i8, 0b0, 0b1000_0000u8 as i8, 0b01,
97+
0b0101, 0b1111_0000u8 as i8, 0, 0,
98+
0, 0, 0b1111_0000u8 as i8, 0b0101,
99+
0b01, 0b1000_0000u8 as i8, 0b0, 0b1000_0000u8 as i8,
100+
);
101+
let r = _mm_movemask_epi8(a);
102+
assert_eq!(r, 0b10100100_00100101);
103+
}
104+
105+
#[target_feature(enable = "avx2")]
106+
unsafe fn test_mm256_movemask_epi8() {
107+
let a = _mm256_set1_epi8(-1);
108+
let r = _mm256_movemask_epi8(a);
109+
let e = -1;
110+
assert_eq!(r, e);
111+
}
112+
113+
#[target_feature(enable = "sse2")]
114+
unsafe fn test_mm_add_epi8() {
115+
let a = _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
116+
let b = _mm_setr_epi8(
117+
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
118+
);
119+
let r = _mm_add_epi8(a, b);
120+
let e = _mm_setr_epi8(
121+
16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46,
122+
);
123+
assert_eq_m128i(r, e);
124+
}
125+
126+
#[target_feature(enable = "sse2")]
127+
unsafe fn test_mm_add_pd() {
128+
let a = _mm_setr_pd(1.0, 2.0);
129+
let b = _mm_setr_pd(5.0, 10.0);
130+
let r = _mm_add_pd(a, b);
131+
assert_eq_m128d(r, _mm_setr_pd(6.0, 12.0));
132+
}
133+
134+
fn assert_eq_m128i(x: std::arch::x86_64::__m128i, y: std::arch::x86_64::__m128i) {
135+
unsafe {
136+
assert_eq!(std::mem::transmute::<_, [u8; 16]>(x), std::mem::transmute::<_, [u8; 16]>(y));
137+
}
138+
}
139+
140+
#[target_feature(enable = "sse2")]
141+
pub unsafe fn assert_eq_m128d(a: __m128d, b: __m128d) {
142+
if _mm_movemask_pd(_mm_cmpeq_pd(a, b)) != 0b11 {
143+
panic!("{:?} != {:?}", a, b);
144+
}
145+
}
146+
147+
#[target_feature(enable = "sse2")]
148+
unsafe fn test_mm_cvtsi128_si64() {
149+
let r = _mm_cvtsi128_si64(std::mem::transmute::<[i64; 2], _>([5, 0]));
150+
assert_eq!(r, 5);
151+
}
152+
153+
#[target_feature(enable = "sse4.1")]
154+
unsafe fn test_mm_cvtepi8_epi16() {
155+
let a = _mm_set1_epi8(10);
156+
let r = _mm_cvtepi8_epi16(a);
157+
let e = _mm_set1_epi16(10);
158+
assert_eq_m128i(r, e);
159+
let a = _mm_set1_epi8(-10);
160+
let r = _mm_cvtepi8_epi16(a);
161+
let e = _mm_set1_epi16(-10);
162+
assert_eq_m128i(r, e);
3163
}
164+
165+
#[target_feature(enable = "sse4.1")]
166+
unsafe fn test_mm_extract_epi8() {
167+
let a = _mm_setr_epi8(
168+
-1, 1, 2, 3, 4, 5, 6, 7,
169+
8, 9, 10, 11, 12, 13, 14, 15
170+
);
171+
let r1 = _mm_extract_epi8(a, 0);
172+
let r2 = _mm_extract_epi8(a, 19);
173+
assert_eq!(r1, 0xFF);
174+
assert_eq!(r2, 3);
175+
}*/

src/back/write.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ pub(crate) unsafe fn codegen(cgcx: &CodegenContext<GccCodegenBackend>, _diag_han
170170
.prof
171171
.generic_activity_with_arg("LLVM_module_codegen_emit_obj", &module.name[..]);
172172
//with_codegen(tm, llmod, config.no_builtins, |cpm| {
173-
println!("1: {}", module.name);
173+
//println!("1: {}", module.name);
174174
match &*module.name {
175175
"std_example.7rcbfp3g-cgu.15" => {
176176
println!("Dumping reproducer {}", module.name);

src/builder.rs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -361,11 +361,16 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
361361
// gccjit requires to use the result of functions, even when it's not used.
362362
// That's why we assign the result to a local or call add_eval().
363363
let gcc_func = func_ptr.get_type().is_function_ptr_type().expect("function ptr");
364-
let return_type = gcc_func.get_return_type();
364+
let mut return_type = gcc_func.get_return_type();
365365
let current_block = self.current_block.borrow().expect("block");
366366
let void_type = self.context.new_type::<()>();
367367
let current_func = current_block.get_function();
368368

369+
// FIXME: As a temporary workaround for unsupported LLVM intrinsics.
370+
if gcc_func.get_param_count() == 0 && format!("{:?}", func_ptr) == "__builtin_ia32_pmovmskb128" {
371+
return_type = self.int_type;
372+
}
373+
369374
if return_type != void_type {
370375
unsafe { RETURN_VALUE_COUNT += 1 };
371376
let result = current_func.new_local(None, return_type, &format!("returnValue{}", unsafe { RETURN_VALUE_COUNT }));
@@ -374,14 +379,16 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
374379
}
375380
else {
376381
if gcc_func.get_param_count() == 0 {
377-
// As a temporary workaround for unsupported LLVM intrinsics.
382+
// FIXME: As a temporary workaround for unsupported LLVM intrinsics.
378383
current_block.add_eval(None, self.cx.context.new_call_through_ptr(None, func_ptr, &[]));
379384
}
380385
else {
381386
current_block.add_eval(None, self.cx.context.new_call_through_ptr(None, func_ptr, &args));
382387
}
383388
// Return dummy value when not having return value.
384-
self.context.new_rvalue_from_long(self.isize_type, 0)
389+
let result = current_func.new_local(None, self.isize_type, "dummyValueThatShouldNeverBeUsed");
390+
current_block.add_assignment(None, result, self.context.new_rvalue_from_long(self.isize_type, 0));
391+
result.to_rvalue()
385392
}
386393
}
387394

@@ -550,7 +557,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
550557
}
551558

552559
fn add(&mut self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> {
553-
if a.get_type() != b.get_type() {
560+
// FIXME: this should not be required.
561+
if format!("{:?}", a.get_type()) != format!("{:?}", b.get_type()) {
554562
b = self.context.new_cast(None, b, a.get_type());
555563
}
556564
a + b
@@ -1158,15 +1166,11 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
11581166
element.get_address(None)
11591167
}
11601168
else if let Some(vector_type) = value_type.is_vector() {
1161-
let count = vector_type.get_num_units();
1162-
let element_type = vector_type.get_element_type();
1163-
let indexes = vec![self.context.new_rvalue_from_long(element_type, i64::try_from(idx).expect("i64::try_from")); count as usize];
1164-
let indexes = self.context.new_rvalue_from_vector(None, value_type, &indexes);
1165-
let variable = self.current_func.borrow().expect("func")
1166-
.new_local(None, value_type, "vectorVar");
1167-
self.current_block.borrow().expect("block")
1168-
.add_assignment(None, variable, value + indexes);
1169-
variable.get_address(None)
1169+
let array_type = vector_type.get_element_type().make_pointer();
1170+
let array = self.bitcast(ptr, array_type);
1171+
let index = self.context.new_rvalue_from_long(self.u64_type, i64::try_from(idx).expect("i64::try_from"));
1172+
let element = self.context.new_array_access(None, array, index);
1173+
element.get_address(None)
11701174
}
11711175
else if let Some(struct_type) = value_type.is_struct() {
11721176
ptr.dereference_field(None, struct_type.get_field(idx as i32)).get_address(None)
@@ -1186,11 +1190,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
11861190
fn sext(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
11871191
// TODO: check that it indeed sign extend the value.
11881192
//println!("Sext {:?} to {:?}", value, dest_ty);
1189-
println!("Value type: {:?}", value.get_type());
1190-
println!("Value type is vec: {:?}", value.get_type().is_vector().is_some());
11911193
//if let Some(vector_type) = value.get_type().is_vector() {
11921194
if let Some(vector_type) = dest_ty.is_vector() {
1193-
println!("Vector unit {:?}", vector_type.get_num_units());
11941195
// TODO: nothing to do as it is only for LLVM?
11951196
return value;
11961197
/*let dest_type = self.context.new_vector_type(dest_ty, vector_type.get_num_units() as u64);

src/declare.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use rustc_target::abi::call::FnAbi;
66

77
use crate::abi::FnAbiGccExt;
88
use crate::context::{CodegenCx, unit_name};
9+
use crate::intrinsic::llvm;
910
use crate::mangled_std_symbols::{ARGV_INIT_ARRAY, ARGV_INIT_WRAPPER};
1011

1112
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
@@ -170,11 +171,8 @@ fn declare_raw_fn<'gcc>(cx: &CodegenCx<'gcc, '_>, name: &str, callconv: () /*llv
170171
llvm::LLVMRustGetOrInsertFunction(cx.llmod, name.as_ptr().cast(), name.len(), ty)
171172
};*/
172173

173-
if name == "llvm.x86.xgetbv" {
174-
// TODO: support other LLVM intrinsics.
175-
let func = cx.context.get_builtin_function("__builtin_trap");
176-
cx.functions.borrow_mut().insert(name.to_string(), func);
177-
return func;
174+
if name.starts_with("llvm.") {
175+
return llvm::intrinsic(name, cx);
178176
}
179177
let func =
180178
if cx.functions.borrow().contains_key(name) {

src/intrinsic/llvm.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use gccjit::{Function, FunctionType};
2+
3+
use crate::context::CodegenCx;
4+
5+
pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function<'gcc> {
6+
let gcc_name =
7+
match name {
8+
"llvm.x86.xgetbv" => {
9+
let gcc_name = "__builtin_trap";
10+
let func = cx.context.get_builtin_function(gcc_name);
11+
cx.functions.borrow_mut().insert(gcc_name.to_string(), func);
12+
return func;
13+
},
14+
// TODO: this doc specifies the equivalent GCC builtins: http://huonw.github.io/llvmint/llvmint/x86/index.html
15+
"llvm.x86.sse2.cmp.pd" => "__builtin_ia32_cmppd",
16+
"llvm.x86.sse2.movmsk.pd" => "__builtin_ia32_movmskpd",
17+
"llvm.x86.sse2.pmovmskb.128" => "__builtin_ia32_pmovmskb128",
18+
_ => unimplemented!("unsupported LLVM intrinsic {}", name)
19+
};
20+
21+
println!("Get target builtin");
22+
let func = cx.context.get_target_builtin_function(gcc_name);
23+
cx.functions.borrow_mut().insert(gcc_name.to_string(), func);
24+
func
25+
}

src/intrinsic/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod llvm;
12
mod simd;
23

34
use gccjit::{ComparisonOp, Function, RValue, ToRValue, Type, UnaryOp};

src/intrinsic/simd.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use crate::builder::Builder;
1313
use crate::context::CodegenCx;
1414

1515
pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, name: Symbol, callee_ty: Ty<'tcx>, args: &[OperandRef<'tcx, RValue<'gcc>>], ret_ty: Ty<'tcx>, llret_ty: Type<'gcc>, span: Span) -> Result<RValue<'gcc>, ()> {
16+
//println!("Generic simd: {}", name);
17+
1618
// macros for error handling:
1719
macro_rules! emit_error {
1820
($msg: tt) => {

src/lib.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen};
6262
use rustc_codegen_ssa::base::codegen_crate;
6363
use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryFn};
6464
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
65+
use rustc_codegen_ssa::target_features::supported_target_features;
6566
use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods};
6667
use rustc_data_structures::fx::FxHashMap;
6768
use rustc_errors::{ErrorReported, Handler};
@@ -130,6 +131,10 @@ impl CodegenBackend for GccCodegenBackend {
130131

131132
Ok(())
132133
}
134+
135+
fn target_features(&self, sess: &Session) -> Vec<Symbol> {
136+
target_features(sess)
137+
}
133138
}
134139

135140
impl ExtraBackendMethods for GccCodegenBackend {
@@ -310,3 +315,25 @@ pub fn target_cpu(sess: &Session) -> &str {
310315
let name = sess.opts.cg.target_cpu.as_ref().unwrap_or(&sess.target.cpu);
311316
handle_native(name)
312317
}
318+
319+
pub fn target_features(sess: &Session) -> Vec<Symbol> {
320+
supported_target_features(sess)
321+
.iter()
322+
.filter_map(
323+
|&(feature, gate)| {
324+
if sess.is_nightly_build() || gate.is_none() { Some(feature) } else { None }
325+
},
326+
)
327+
.filter(|feature| {
328+
if feature.starts_with("sse") {
329+
return true;
330+
}
331+
//println!("Feature: {}", feature);
332+
/*let llvm_feature = to_llvm_feature(sess, feature);
333+
let cstr = CString::new(llvm_feature).unwrap();
334+
unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) }*/
335+
false
336+
})
337+
.map(|feature| Symbol::intern(feature))
338+
.collect()
339+
}

0 commit comments

Comments
 (0)