Skip to content

Constant Vector Operations #109

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions Sources/LLVM/Constant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public enum Signed: IntegralConstantRepresentation {}
public enum Floating: NumericalConstantRepresentation {}
/// Represents struct types and operations.
public enum Struct: ConstantRepresentation {}
/// Represents vector types and operations.
public enum Vector: ConstantRepresentation {}

/// A `Constant` represents a value initialized to a constant. Constant values
/// may be manipulated with standard Swift arithmetic operations and used with
Expand Down Expand Up @@ -952,6 +954,27 @@ extension Constant where Repr == Struct {
}
}

// MARK: Vector Operations

extension Constant where Repr == Vector {
/// Builds a constant operation to construct a permutation of elements
/// from the two given input vectors, returning a vector with the same element
/// type as the inputs and length that is the same as the shuffle mask.
///
/// - parameter vector1: The first constant vector to shuffle.
/// - parameter vector2: The second constant vector to shuffle.
/// - parameter mask: A constant vector of `i32` values that acts as a mask
/// for the shuffled vectors.
///
/// - returns: A value representing a constant vector with the same element
/// type as the inputs and length that is the same as the shuffle mask.
public static func buildShuffleVector(_ vector1: Constant, and vector2: Constant, mask: Constant) -> Constant {
guard let maskTy = mask.type as? VectorType, maskTy.elementType is IntType else {
fatalError("Vector shuffle mask's elements must be 32-bit integers")
}
return Constant(llvm: LLVMConstShuffleVector(vector1.asLLVM(), vector2.asLLVM(), mask.asLLVM()))
}
}

// MARK: Swift Operators

Expand Down Expand Up @@ -1384,3 +1407,49 @@ extension Constant where Repr: IntegralConstantRepresentation {
return Constant(llvm: LLVMConstNot(lhs.llvm))
}
}

// MARK: Undef

extension Constant where Repr: IntegralConstantRepresentation {
/// Returns the special LLVM `undef` value for this type.
///
/// The `undef` value can be used anywhere a constant is expected, and
/// indicates that the user of the value may receive an unspecified
/// bit-pattern.
public static func undef(_ ty: IntType) -> Constant {
return Constant<Repr>(llvm: ty.undef().asLLVM())
}
}

extension Constant where Repr == Floating {
/// Returns the special LLVM `undef` value for this type.
///
/// The `undef` value can be used anywhere a constant is expected, and
/// indicates that the user of the value may receive an unspecified
/// bit-pattern.
public static func undef(_ ty: FloatType) -> Constant {
return Constant(llvm: ty.undef().asLLVM())
}
}

extension Constant where Repr == Struct {
/// Returns the special LLVM `undef` value for this type.
///
/// The `undef` value can be used anywhere a constant is expected, and
/// indicates that the user of the value may receive an unspecified
/// bit-pattern.
public static func undef(_ ty: StructType) -> Constant {
return Constant(llvm: ty.undef().asLLVM())
}
}

extension Constant where Repr == Vector {
/// Returns the special LLVM `undef` value for this type.
///
/// The `undef` value can be used anywhere a constant is expected, and
/// indicates that the user of the value may receive an unspecified
/// bit-pattern.
public static func undef(_ ty: VectorType) -> Constant {
return Constant(llvm: ty.undef().asLLVM())
}
}
17 changes: 17 additions & 0 deletions Sources/LLVM/VectorType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,23 @@ public struct VectorType: IRType {
self.count = count
}

/// Creates a constant value of this vector type initialized with the given
/// list of values.
///
/// - precondition: values.count == vector.count
/// - parameter values: A list of values of elements of this vector.
///
/// - returns: A value representing a constant value of this vector type.
public func constant(_ values: [IRValue]) -> Constant<Vector> {
assert(numericCast(values.count) == LLVMGetVectorSize(asLLVM()),
"The number of values must match the number of elements in the vector")
var vals = values.map { $0.asLLVM() as Optional }
return vals.withUnsafeMutableBufferPointer { buf in
return Constant(llvm: LLVMConstVector(buf.baseAddress,
UInt32(buf.count)))
}
}

/// Retrieves the underlying LLVM type object.
public func asLLVM() -> LLVMTypeRef {
return LLVMVectorType(elementType.asLLVM(), UInt32(count))
Expand Down
56 changes: 56 additions & 0 deletions Tests/LLVMTests/ConstantSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,62 @@ class ConstantSpec : XCTestCase {
// STRUCTCONSTGETELEMENT-NEXT: }
module.dump()
})

XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["VECTORCONSTSHUFFLE-IDENTITY"]) {
// VECTORCONSTSHUFFLE-IDENTITY: ; ModuleID = '[[ModuleName:ConstantTest]]'
// VECTORCONSTSHUFFLE-IDENTITY-NEXT: source_filename = "[[ModuleName]]"
let module = Module(name: "ConstantTest")
let builder = IRBuilder(module: module)

let vecTy = VectorType(elementType: IntType.int32, count: 4)
// VECTORCONSTSHUFFLE-IDENTITY: define <4 x i32> @main() {
let main = builder.addFunction("main",
type: FunctionType(argTypes: [],
returnType: vecTy))

let vec1 = vecTy.constant([ 1, 2, 3, 4 ] as [Int32])
let mask = vecTy.constant([ 0, 1, 2, 3 ] as [Int32])

// VECTORCONSTSHUFFLE-IDENTITY-NEXT: entry:
let entry = main.appendBasicBlock(named: "entry")
builder.positionAtEnd(of: entry)

let firstElement = Constant<Vector>.buildShuffleVector(vec1, and: .undef(vecTy), mask: mask)

// VECTORCONSTSHUFFLE-IDENTITY-NEXT: ret <4 x i32> <i32 1, i32 2, i32 3, i32 4>
builder.buildRet(firstElement)
// VECTORCONSTSHUFFLE-IDENTITY-NEXT: }
module.dump()
})

XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["VECTORCONSTSHUFFLE"]) {
// VECTORCONSTSHUFFLE: ; ModuleID = '[[ModuleName:ConstantTest]]'
// VECTORCONSTSHUFFLE-NEXT: source_filename = "[[ModuleName]]"
let module = Module(name: "ConstantTest")
let builder = IRBuilder(module: module)

let maskTy = VectorType(elementType: IntType.int32, count: 8)
// VECTORCONSTSHUFFLE: define <8 x i32> @main() {
let main = builder.addFunction("main",
type: FunctionType(argTypes: [],
returnType: maskTy))

let vecTy = VectorType(elementType: IntType.int32, count: 4)
let vec1 = vecTy.constant([ 1, 2, 3, 4 ] as [Int32])
let vec2 = vecTy.constant([ 5, 6, 7, 8 ] as [Int32])
let mask = maskTy.constant([ 1, 3, 5, 7, 0, 2, 4, 6 ] as [Int32])

// VECTORCONSTSHUFFLE-NEXT: entry:
let entry = main.appendBasicBlock(named: "entry")
builder.positionAtEnd(of: entry)

let firstElement = Constant<Vector>.buildShuffleVector(vec1, and: vec2, mask: mask)

// VECTORCONSTSHUFFLE-NEXT: ret <8 x i32> <i32 2, i32 4, i32 6, i32 8, i32 1, i32 3, i32 5, i32 7>
builder.buildRet(firstElement)
// VECTORCONSTSHUFFLE-NEXT: }
module.dump()
})
}

#if !os(macOS)
Expand Down
56 changes: 39 additions & 17 deletions Tests/LLVMTests/IRBuilderSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ class IRBuilderSpec : XCTestCase {
var g2 = builder.addGlobal("b", type: IntType.int32)
g2.initializer = Int32(1)

// IRBUILDERARITH-NEXT: @vec1 = global <8 x i64> <i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1>
// IRBUILDERARITH-NEXT: @vec2 = global <8 x i64> <i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2, i64 2>
let vecTy = VectorType(elementType: IntType.int32, count: 8)
let gVec1 = builder.addGlobal("vec1", initializer: vecTy.constant([ 1, 1, 1, 1, 1, 1, 1, 1 ]))
let gVec2 = builder.addGlobal("vec2", initializer: vecTy.constant([ 2, 2, 2, 2, 2, 2, 2, 2 ]))

// IRBUILDERARITH: define void @main() {
let main = builder.addFunction("main",
type: FunctionType(argTypes: [],
Expand All @@ -46,44 +52,60 @@ class IRBuilderSpec : XCTestCase {
let entry = main.appendBasicBlock(named: "entry")
builder.positionAtEnd(of: entry)

// IRBUILDERARITH-NEXT: %0 = load i32, i32* @a
// IRBUILDERARITH-NEXT: [[A_LOAD:%[0-9]+]] = load i32, i32* @a
let vg1 = builder.buildLoad(g1)
// IRBUILDERARITH-NEXT: %1 = load i32, i32* @b
// IRBUILDERARITH-NEXT: [[B_LOAD:%[0-9]+]] = load i32, i32* @b
let vg2 = builder.buildLoad(g2)

// IRBUILDERARITH-NEXT: %2 = add i32 %0, %1
// IRBUILDERARITH-NEXT: [[VEC1_LOAD:%[0-9]+]] = load <8 x i64>, <8 x i64>* @vec1
let vgVec1 = builder.buildLoad(gVec1)

// IRBUILDERARITH-NEXT: [[VEC2_LOAD:%[0-9]+]] = load <8 x i64>, <8 x i64>* @vec2
let vgVec2 = builder.buildLoad(gVec2)

// IRBUILDERARITH-NEXT: {{%[0-9]+}} = add i32 [[A_LOAD]], [[B_LOAD]]
_ = builder.buildAdd(vg1, vg2)
// IRBUILDERARITH-NEXT: %3 = sub i32 %0, %1
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = sub i32 [[A_LOAD]], [[B_LOAD]]
_ = builder.buildSub(vg1, vg2)
// IRBUILDERARITH-NEXT: %4 = mul i32 %0, %1
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = mul i32 [[A_LOAD]], [[B_LOAD]]
_ = builder.buildMul(vg1, vg2)
// IRBUILDERARITH-NEXT: %5 = sdiv i32 %0, %1
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = sdiv i32 [[A_LOAD]], [[B_LOAD]]
_ = builder.buildDiv(vg1, vg2, signed: true)
// IRBUILDERARITH-NEXT: %6 = udiv i32 %0, %1
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = udiv i32 [[A_LOAD]], [[B_LOAD]]
_ = builder.buildDiv(vg1, vg2, signed: false)

// IRBUILDERARITH-NEXT: %7 = add nsw i32 %0, %1
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = add nsw i32 [[A_LOAD]], [[B_LOAD]]
_ = builder.buildAdd(vg1, vg2, overflowBehavior: .noSignedWrap)
// IRBUILDERARITH-NEXT: %8 = sub nsw i32 %0, %1
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = sub nsw i32 [[A_LOAD]], [[B_LOAD]]
_ = builder.buildSub(vg1, vg2, overflowBehavior: .noSignedWrap)
// IRBUILDERARITH-NEXT: %9 = mul nsw i32 %0, %1
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = mul nsw i32 [[A_LOAD]], [[B_LOAD]]
_ = builder.buildMul(vg1, vg2, overflowBehavior: .noSignedWrap)

// IRBUILDERARITH-NEXT: %10 = add nuw i32 %0, %1
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = add nuw i32 [[A_LOAD]], [[B_LOAD]]
_ = builder.buildAdd(vg1, vg2, overflowBehavior: .noUnsignedWrap)
// IRBUILDERARITH-NEXT: %11 = sub nuw i32 %0, %1
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = sub nuw i32 [[A_LOAD]], [[B_LOAD]]
_ = builder.buildSub(vg1, vg2, overflowBehavior: .noUnsignedWrap)
// IRBUILDERARITH-NEXT: %12 = mul nuw i32 %0, %1
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = mul nuw i32 [[A_LOAD]], [[B_LOAD]]
_ = builder.buildMul(vg1, vg2, overflowBehavior: .noUnsignedWrap)

// IRBUILDERARITH-NEXT: %13 = sub i32 0, %0
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = add <8 x i64> [[VEC1_LOAD]], [[VEC2_LOAD]]
_ = builder.buildAdd(vgVec1, vgVec2)
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = sub <8 x i64> [[VEC1_LOAD]], [[VEC2_LOAD]]
_ = builder.buildSub(vgVec1, vgVec2)
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = mul <8 x i64> [[VEC1_LOAD]], [[VEC2_LOAD]]
_ = builder.buildMul(vgVec1, vgVec2)
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = sdiv <8 x i64> [[VEC1_LOAD]], [[VEC2_LOAD]]
_ = builder.buildDiv(vgVec1, vgVec2, signed: true)
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = udiv <8 x i64> [[VEC1_LOAD]], [[VEC2_LOAD]]
_ = builder.buildDiv(vgVec1, vgVec2, signed: false)

// IRBUILDERARITH-NEXT: {{%[0-9]+}} = sub i32 0, [[A_LOAD]]
_ = builder.buildNeg(vg1, overflowBehavior: .default)
// IRBUILDERARITH-NEXT: %14 = sub nuw i32 0, %0
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = sub nuw i32 0, [[A_LOAD]]
_ = builder.buildNeg(vg1, overflowBehavior: .noUnsignedWrap)
// IRBUILDERARITH-NEXT: %15 = sub nsw i32 0, %0
// IRBUILDERARITH-NEXT: {{%[0-9]+}} = sub nsw i32 0, [[A_LOAD]]
_ = builder.buildNeg(vg1, overflowBehavior: .noSignedWrap)


// IRBUILDERARITH-NEXT: ret void
builder.buildRetVoid()
// IRBUILDERARITH-NEXT: }
Expand Down