Skip to content

Commit 21afcc4

Browse files
committed
Added left, logical right, and sign extended right shift builder support.
Also added tests, doc tests, and LoC to README
1 parent 018c54c commit 21afcc4

File tree

3 files changed

+226
-5
lines changed

3 files changed

+226
-5
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
[![Crates.io](https://img.shields.io/crates/v/inkwell.svg?style=plastic)](https://crates.io/crates/inkwell)
44
[![Build Status](https://travis-ci.org/TheDan64/inkwell.svg?branch=master)](https://travis-ci.org/TheDan64/inkwell)
55
[![codecov](https://codecov.io/gh/TheDan64/inkwell/branch/master/graph/badge.svg)](https://codecov.io/gh/TheDan64/inkwell)
6+
[![lines of code](https://tokei.rs/b1/github/TheDan64/inkwell)](https://github.com/Aaronepower/tokei)
67

78
**I**t's a **N**ew **K**ind of **W**rapper for **E**xposing **LL**VM (*S*afely)
89

src/builder.rs

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use either::Either;
2-
use llvm_sys::core::{LLVMBuildAdd, LLVMBuildAlloca, LLVMBuildAnd, LLVMBuildArrayAlloca, LLVMBuildArrayMalloc, LLVMBuildBr, LLVMBuildCall, LLVMBuildCast, LLVMBuildCondBr, LLVMBuildExtractValue, LLVMBuildFAdd, LLVMBuildFCmp, LLVMBuildFDiv, LLVMBuildFence, LLVMBuildFMul, LLVMBuildFNeg, LLVMBuildFree, LLVMBuildFSub, LLVMBuildGEP, LLVMBuildICmp, LLVMBuildInsertValue, LLVMBuildIsNotNull, LLVMBuildIsNull, LLVMBuildLoad, LLVMBuildMalloc, LLVMBuildMul, LLVMBuildNeg, LLVMBuildNot, LLVMBuildOr, LLVMBuildPhi, LLVMBuildPointerCast, LLVMBuildRet, LLVMBuildRetVoid, LLVMBuildStore, LLVMBuildSub, LLVMBuildUDiv, LLVMBuildUnreachable, LLVMBuildXor, LLVMDisposeBuilder, LLVMGetElementType, LLVMGetInsertBlock, LLVMGetReturnType, LLVMGetTypeKind, LLVMInsertIntoBuilder, LLVMPositionBuilderAtEnd, LLVMTypeOf, LLVMSetTailCall, LLVMBuildExtractElement, LLVMBuildInsertElement, LLVMBuildIntToPtr, LLVMBuildPtrToInt, LLVMInsertIntoBuilderWithName, LLVMClearInsertionPosition, LLVMCreateBuilder, LLVMPositionBuilder, LLVMPositionBuilderBefore, LLVMBuildAggregateRet, LLVMBuildStructGEP, LLVMBuildInBoundsGEP, LLVMBuildPtrDiff, LLVMBuildNSWAdd, LLVMBuildNUWAdd, LLVMBuildNSWSub, LLVMBuildNUWSub, LLVMBuildNSWMul, LLVMBuildNUWMul, LLVMBuildSDiv, LLVMBuildSRem, LLVMBuildURem, LLVMBuildFRem, LLVMBuildNSWNeg, LLVMBuildNUWNeg, LLVMBuildFPToUI, LLVMBuildFPToSI, LLVMBuildSIToFP, LLVMBuildUIToFP, LLVMBuildFPTrunc, LLVMBuildFPExt, LLVMBuildIntCast, LLVMBuildFPCast, LLVMBuildSExtOrBitCast, LLVMBuildZExtOrBitCast, LLVMBuildTruncOrBitCast, LLVMBuildSwitch, LLVMAddCase};
2+
use llvm_sys::core::{LLVMBuildAdd, LLVMBuildAlloca, LLVMBuildAnd, LLVMBuildArrayAlloca, LLVMBuildArrayMalloc, LLVMBuildBr, LLVMBuildCall, LLVMBuildCast, LLVMBuildCondBr, LLVMBuildExtractValue, LLVMBuildFAdd, LLVMBuildFCmp, LLVMBuildFDiv, LLVMBuildFence, LLVMBuildFMul, LLVMBuildFNeg, LLVMBuildFree, LLVMBuildFSub, LLVMBuildGEP, LLVMBuildICmp, LLVMBuildInsertValue, LLVMBuildIsNotNull, LLVMBuildIsNull, LLVMBuildLoad, LLVMBuildMalloc, LLVMBuildMul, LLVMBuildNeg, LLVMBuildNot, LLVMBuildOr, LLVMBuildPhi, LLVMBuildPointerCast, LLVMBuildRet, LLVMBuildRetVoid, LLVMBuildStore, LLVMBuildSub, LLVMBuildUDiv, LLVMBuildUnreachable, LLVMBuildXor, LLVMDisposeBuilder, LLVMGetElementType, LLVMGetInsertBlock, LLVMGetReturnType, LLVMGetTypeKind, LLVMInsertIntoBuilder, LLVMPositionBuilderAtEnd, LLVMTypeOf, LLVMSetTailCall, LLVMBuildExtractElement, LLVMBuildInsertElement, LLVMBuildIntToPtr, LLVMBuildPtrToInt, LLVMInsertIntoBuilderWithName, LLVMClearInsertionPosition, LLVMCreateBuilder, LLVMPositionBuilder, LLVMPositionBuilderBefore, LLVMBuildAggregateRet, LLVMBuildStructGEP, LLVMBuildInBoundsGEP, LLVMBuildPtrDiff, LLVMBuildNSWAdd, LLVMBuildNUWAdd, LLVMBuildNSWSub, LLVMBuildNUWSub, LLVMBuildNSWMul, LLVMBuildNUWMul, LLVMBuildSDiv, LLVMBuildSRem, LLVMBuildURem, LLVMBuildFRem, LLVMBuildNSWNeg, LLVMBuildNUWNeg, LLVMBuildFPToUI, LLVMBuildFPToSI, LLVMBuildSIToFP, LLVMBuildUIToFP, LLVMBuildFPTrunc, LLVMBuildFPExt, LLVMBuildIntCast, LLVMBuildFPCast, LLVMBuildSExtOrBitCast, LLVMBuildZExtOrBitCast, LLVMBuildTruncOrBitCast, LLVMBuildSwitch, LLVMAddCase, LLVMBuildShl, LLVMBuildAShr, LLVMBuildLShr};
33
use llvm_sys::prelude::{LLVMBuilderRef, LLVMValueRef};
44
use llvm_sys::{LLVMOpcode, LLVMIntPredicate, LLVMTypeKind, LLVMRealPredicate, LLVMAtomicOrdering};
55

@@ -500,6 +500,132 @@ impl Builder {
500500
IntValue::new(value)
501501
}
502502

503+
/// Builds an `IntValue` containing the result of a logical left shift instruction.
504+
///
505+
/// # Example
506+
/// A logical left shift is an operation in which an integer value's bits are shifted left by N number of positions.
507+
///
508+
/// ```rust
509+
/// assert_eq!(0b0000_0001 << 0, 0b0000_0001);
510+
/// assert_eq!(0b0000_0001 << 1, 0b0000_0010);
511+
/// assert_eq!(0b0000_0011 << 2, 0b0000_1100);
512+
/// ```
513+
///
514+
/// In Rust, a function that could do this for 8bit values looks like:
515+
///
516+
/// ```rust
517+
/// fn left_shift(value: u8, n: u8) -> u8 {
518+
/// value << n
519+
/// }
520+
/// ```
521+
///
522+
/// And in Inkwell, the corresponding function would look roughly like:
523+
///
524+
/// ```rust
525+
/// use inkwell::context::Context;
526+
///
527+
/// // Setup
528+
/// let context = Context::create();
529+
/// let module = context.create_module("my_module");
530+
/// let builder = context.create_builder();
531+
/// let i8_type = context.i8_type();
532+
/// let fn_type = i8_type.fn_type(&[&i8_type, &i8_type], false);
533+
///
534+
/// // Function Definition
535+
/// let function = module.add_function("left_shift", &fn_type, None);
536+
/// let value = function.get_first_param().unwrap().into_int_value();
537+
/// let n = function.get_nth_param(1).unwrap().into_int_value();
538+
/// let entry_block = function.append_basic_block("entry");
539+
///
540+
/// builder.position_at_end(&entry_block);
541+
///
542+
/// let shift = builder.build_left_shift(&value, &n, "left_shift"); // value << n
543+
///
544+
/// builder.build_return(Some(&shift));
545+
/// ```
546+
pub fn build_left_shift(&self, lhs: &IntValue, rhs: &IntValue, name: &str) -> IntValue {
547+
let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");
548+
549+
let value = unsafe {
550+
LLVMBuildShl(self.builder, lhs.as_value_ref(), rhs.as_value_ref(), c_string.as_ptr())
551+
};
552+
553+
IntValue::new(value)
554+
}
555+
556+
/// Builds an `IntValue` containing the result of a right shift instruction.
557+
///
558+
/// # Example
559+
/// A right shift is an operation in which an integer value's bits are shifted right by N number of positions.
560+
/// It may either be logical and have its leftmost N bit(s) filled with zeros or sign extended and filled with ones
561+
/// if the leftmost bit was one.
562+
///
563+
/// ```rust
564+
/// // Logical Right Shift
565+
/// assert_eq!(0b1100_0000 >> 2, 0b0011_0000);
566+
/// assert_eq!(0b0000_0010 >> 1, 0b0000_0001);
567+
/// assert_eq!(0b0000_1100 >> 2, 0b0000_0011);
568+
///
569+
/// // Sign Extended Right Shift
570+
/// assert_eq!(0b0100_0000i8 >> 2, 0b0001_0000);
571+
/// assert_eq!(0b1110_0000i8 >> 1, 0b1111_0000);
572+
/// assert_eq!(0b1100_0000i8 >> 2, 0b1111_0000);
573+
/// ```
574+
///
575+
/// In Rust, functions that could do this for 8bit values look like:
576+
///
577+
/// ```rust
578+
/// fn logical_right_shift(value: u8, n: u8) -> u8 {
579+
/// value >> n
580+
/// }
581+
///
582+
/// fn sign_extended_right_shift(value: i8, n: u8) -> i8 {
583+
/// value >> n
584+
/// }
585+
/// ```
586+
/// Notice that, in Rust (and most other languages), whether or not a value is sign extended depends wholly on whether
587+
/// or not the type is signed (ie an i8 is a signed 8 bit value). LLVM does not make this distinction for you.
588+
///
589+
/// In Inkwell, the corresponding functions would look roughly like:
590+
///
591+
/// ```rust
592+
/// use inkwell::context::Context;
593+
///
594+
/// // Setup
595+
/// let context = Context::create();
596+
/// let module = context.create_module("my_module");
597+
/// let builder = context.create_builder();
598+
/// let i8_type = context.i8_type();
599+
/// let fn_type = i8_type.fn_type(&[&i8_type, &i8_type], false);
600+
///
601+
/// // Function Definition
602+
/// let function = module.add_function("right_shift", &fn_type, None);
603+
/// let value = function.get_first_param().unwrap().into_int_value();
604+
/// let n = function.get_nth_param(1).unwrap().into_int_value();
605+
/// let entry_block = function.append_basic_block("entry");
606+
///
607+
/// builder.position_at_end(&entry_block);
608+
///
609+
/// // Whether or not your right shift is sign extended (true) or logical (false) depends
610+
/// // on the boolean input parameter:
611+
/// let shift = builder.build_right_shift(&value, &n, false, "right_shift"); // value >> n
612+
///
613+
/// builder.build_return(Some(&shift));
614+
/// ```
615+
pub fn build_right_shift(&self, lhs: &IntValue, rhs: &IntValue, sign_extend: bool, name: &str) -> IntValue {
616+
let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");
617+
618+
let value = unsafe {
619+
if sign_extend {
620+
LLVMBuildAShr(self.builder, lhs.as_value_ref(), rhs.as_value_ref(), c_string.as_ptr())
621+
} else {
622+
LLVMBuildLShr(self.builder, lhs.as_value_ref(), rhs.as_value_ref(), c_string.as_ptr())
623+
}
624+
};
625+
626+
IntValue::new(value)
627+
}
628+
503629
// SubType: <I>(&self, lhs: &IntValue<I>, rhs: &IntValue<I>, name: &str) -> IntValue<I> {
504630
pub fn build_int_sub(&self, lhs: &IntValue, rhs: &IntValue, name: &str) -> IntValue {
505631
let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");

tests/test_builder.rs

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,11 +199,9 @@ fn test_switch() {
199199
// fn switch(val: u8) -> u8 {
200200
// if val == 0 {
201201
// 1
202-
// }
203-
// else if val == 42 {
202+
// } else if val == 42 {
204203
// 255
205-
// }
206-
// else {
204+
// } else {
207205
// val * 2
208206
// }
209207
// }
@@ -246,3 +244,99 @@ fn test_switch() {
246244
assert_eq!(switch(10), 20);
247245
assert_eq!(switch(42), 255);
248246
}
247+
248+
#[test]
249+
fn test_bit_shifts() {
250+
Target::initialize_native(&InitializationConfig::default()).expect("Failed to initialize native target");
251+
252+
let context = Context::create();
253+
let module = context.create_module("unsafe");
254+
let builder = context.create_builder();
255+
let execution_engine = module.create_jit_execution_engine(0).unwrap();
256+
let module = execution_engine.get_module_at(0);
257+
258+
// Here we're going to create a function which looks roughly like:
259+
// fn left_shift(value: u8, bits: u8) -> u8 {
260+
// value << bits
261+
// }
262+
let i8_type = context.i8_type();
263+
let fn_type = i8_type.fn_type(&[&i8_type, &i8_type], false);
264+
let fn_value = module.add_function("left_shift", &fn_type, None);
265+
let value = fn_value.get_first_param().unwrap().into_int_value();
266+
let bits = fn_value.get_nth_param(1).unwrap().into_int_value();
267+
268+
let entry = fn_value.append_basic_block("entry");
269+
270+
builder.position_at_end(&entry);
271+
272+
let shift = builder.build_left_shift(&value, &bits, "shl");
273+
274+
builder.build_return(Some(&shift));
275+
276+
// Here we're going to create a function which looks roughly like:
277+
// fn right_shift(value: u8, bits: u8) -> u8 {
278+
// value >> bits
279+
// }
280+
let fn_value = module.add_function("right_shift", &fn_type, None);
281+
let value = fn_value.get_first_param().unwrap().into_int_value();
282+
let bits = fn_value.get_nth_param(1).unwrap().into_int_value();
283+
284+
let entry = fn_value.append_basic_block("entry");
285+
286+
builder.position_at_end(&entry);
287+
288+
let shift = builder.build_right_shift(&value, &bits, false, "shr");
289+
290+
builder.build_return(Some(&shift));
291+
292+
// Here we're going to create a function which looks roughly like:
293+
// fn right_shift(value: u8, bits: u8) -> u8 {
294+
// value >> bits
295+
// }
296+
let fn_value = module.add_function("right_shift_sign_extend", &fn_type, None);
297+
let value = fn_value.get_first_param().unwrap().into_int_value();
298+
let bits = fn_value.get_nth_param(1).unwrap().into_int_value();
299+
300+
let entry = fn_value.append_basic_block("entry");
301+
302+
builder.position_at_end(&entry);
303+
304+
let shift = builder.build_right_shift(&value, &bits, true, "shr");
305+
306+
builder.build_return(Some(&shift));
307+
308+
let addr = execution_engine.get_function_address("left_shift").unwrap();
309+
let left_shift: extern "C" fn(u8, u8) -> u8 = unsafe { transmute(addr) };
310+
let addr = execution_engine.get_function_address("right_shift").unwrap();
311+
let right_shift: extern "C" fn(u8, u8) -> u8 = unsafe { transmute(addr) };
312+
let addr = execution_engine.get_function_address("right_shift_sign_extend").unwrap();
313+
let right_shift_sign_extend: extern "C" fn(i8, u8) -> i8 = unsafe { transmute(addr) };
314+
315+
assert_eq!(left_shift(0, 0), 0);
316+
assert_eq!(left_shift(0, 4), 0);
317+
assert_eq!(left_shift(1, 0), 1);
318+
assert_eq!(left_shift(1, 1), 2);
319+
assert_eq!(left_shift(1, 2), 4);
320+
assert_eq!(left_shift(1, 3), 8);
321+
assert_eq!(left_shift(64, 1), 128);
322+
323+
assert_eq!(right_shift(128, 1), 64);
324+
assert_eq!(right_shift(8, 3), 1);
325+
assert_eq!(right_shift(4, 2), 1);
326+
assert_eq!(right_shift(2, 1), 1);
327+
assert_eq!(right_shift(1, 0), 1);
328+
assert_eq!(right_shift(0, 4), 0);
329+
assert_eq!(right_shift(0, 0), 0);
330+
331+
assert_eq!(right_shift_sign_extend(8, 3), 1);
332+
assert_eq!(right_shift_sign_extend(4, 2), 1);
333+
assert_eq!(right_shift_sign_extend(2, 1), 1);
334+
assert_eq!(right_shift_sign_extend(1, 0), 1);
335+
assert_eq!(right_shift_sign_extend(0, 4), 0);
336+
assert_eq!(right_shift_sign_extend(0, 0), 0);
337+
assert_eq!(right_shift_sign_extend(-127, 1), -64);
338+
assert_eq!(right_shift_sign_extend(-127, 8), -1);
339+
assert_eq!(right_shift_sign_extend(-65, 3), -9);
340+
assert_eq!(right_shift_sign_extend(-64, 3), -8);
341+
assert_eq!(right_shift_sign_extend(-63, 3), -8);
342+
}

0 commit comments

Comments
 (0)