Skip to content

Commit

Permalink
Merge pull request #81 from Sparrow-lang/parallel_fix
Browse files Browse the repository at this point in the history
Parallel fix
  • Loading branch information
lucteo authored Mar 31, 2019
2 parents 49844c2 + d75e045 commit 5f4134b
Show file tree
Hide file tree
Showing 40 changed files with 1,781 additions and 1,835 deletions.
4 changes: 2 additions & 2 deletions .travis/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ echo "LC_ALL=$LC_ALL"

if [ $TRAVIS_OS_NAME == osx ]; then
# brew update
brew upgrade boost
brew install llvm
# brew upgrade boost
brew install llvm@7
fi
2 changes: 1 addition & 1 deletion .travis/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set -x
CFG_PARAMS=

if [ $TRAVIS_OS_NAME == osx ]; then
CFG_PARAMS="-DLLVM_DIR=/usr/local/opt/llvm/lib/cmake/llvm"
CFG_PARAMS="-DLLVM_DIR=/usr/local/opt/llvm@7/lib/cmake/llvm"
fi

if [ $TRAVIS_OS_NAME == linux ]; then
Expand Down
4 changes: 2 additions & 2 deletions SparrowImplicitLib/par/atomic.llvm
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
define i32 @_Atomic_load32(i32* %atomic) {
%1 = load atomic i32* %atomic seq_cst, align 4
%1 = load atomic i32, i32* %atomic seq_cst, align 4
ret i32 %1
}

Expand All @@ -24,7 +24,7 @@ define i32 @_Atomic_fetchAndAdd32(i32* %atomic, i32 %val) {


define i64 @_Atomic_load64(i64* %atomic) {
%1 = load atomic i64* %atomic seq_cst, align 8
%1 = load atomic i64, i64* %atomic seq_cst, align 8
ret i64 %1
}

Expand Down
231 changes: 95 additions & 136 deletions SparrowImplicitLib/par/atomic.spr
Original file line number Diff line number Diff line change
@@ -1,143 +1,102 @@
module par.atomic;
module par.atomic

import config
import "atomic.llvm"

using AtomicInt = Atomic(Int)
using AtomicLong = Atomic(Long)

concept AtomicType(x) if x._IsAtomicType
concept AtomicInteger(x) if x._IsAtomicType && Integer(#$x.ValueType)
concept NonStdAtomicType(x) if x._IsAtomicType && x.ValueType != x._UnderlyingType

datatype Atomic(T: Type) if sizeOf(T) <= 8
using _IsAtomicType = true
using ValueType = T
using _UnderlyingType = _Impl.AtomicTypeTraits(T)._UnderlyingType

_value: _UnderlyingType

[protected]
fun ctor(this: @Atomic, v: this.ValueType)
_value ctor _toUnderlying(this, v)

fun =(lhs: @AtomicType, rhs: typeOf(lhs))
lhs store (rhs load)
//! Stores a value inside the given atomic, using the assignment operator
fun =(this: @AtomicType, val: this.ValueType) = this store val

//! Loads the value from an atomic; returns a non-atomic value
[native("_Atomic_load32")] fun load(this: @AtomicInt): Int;
[native("_Atomic_load64")] fun load(this: @AtomicLong): Long;
fun load(this: @NonStdAtomicType): ValueType \
= this._fromUnderlying(this._baseAtomic load)

//! Stores a value inside the given atomic
[native("_Atomic_store32")] fun store(this: @AtomicInt, newVal: Int);
[native("_Atomic_store64")] fun store(this: @AtomicLong, newVal: Long);
fun store(this: @NonStdAtomicType, newVal: AnyType)
this._baseAtomic store this._toUnderlying(newVal)

//! Fetches the current value of an atomic, and stores a new value in the atomic
[native("_Atomic_fetchAndStore32")] fun fetchAndStore(x: @AtomicInt, newVal: Int): Int;
[native("_Atomic_fetchAndStore64")] fun fetchAndStore(x: @AtomicLong, newVal: Long): Long;
fun fetchAndStore(x: @NonStdAtomicType, newVal: AnyType): x.ValueType \
= x._fromUnderlying(x._baseAtomic fetchAndStore x._toUnderlying(newVal))

//! Fetch the value of the atomic, and then add the given value to it
[native("_Atomic_fetchAndAdd32")] fun fetchAndAdd(x: @AtomicInt, val: Int): Int;
[native("_Atomic_fetchAndAdd64")] fun fetchAndAdd(x: @AtomicLong, val: Long): Long;
fun fetchAndAdd(x: @NonStdAtomicType, val: AnyType): x.ValueType \
= x._fromUnderlying(x._baseAtomic fetchAndAdd x._toUnderlying(val)) \
if Integer(#$x.ValueType)

//! Fetch the value of the atomic and the increment it
fun fetchAndIncrement(x: @AtomicInteger) = x fetchAndAdd 1
//! Fetch the value of the atomic and the decrement it
fun fetchAndDecrement(x: @AtomicInteger) = x fetchAndAdd -1

//! Adds the given value to the atomic
fun += (x: @AtomicInteger, val: AnyType) { x fetchAndAdd val; }
//! Subtracts the given value to the atomic
fun -= (x: @AtomicInteger, val: AnyType) { x fetchAndAdd (-val); }

//! Increment the atomic value; returns the new value
fun pre_++(x: @AtomicInteger): x.ValueType = (x fetchAndIncrement)+1
//! Increment the atomic value; returns the old value
fun post_++(x: @AtomicInteger): x.ValueType = (x fetchAndIncrement)

//! Decrement the atomic value; returns the new value
fun pre_--(x: @AtomicInteger): x.ValueType = (x fetchAndDecrement)-1
//! Decrement the atomic value; returns the old value
fun post_--(x: @AtomicInteger): x.ValueType = (x fetchAndDecrement)

fun _toUnderlying(this: @Atomic, val: this.ValueType): this._UnderlyingType
var res: _UnderlyingType
reinterpretCast(@ValueType, res) = val
return res
fun _fromUnderlying(this: @Atomic, val: this._UnderlyingType): this.ValueType
var res: ValueType
res = reinterpretCast(@ValueType, val)
return res
fun _baseAtomic(this: @Atomic): @Atomic(_UnderlyingType) = reinterpretCast(@Atomic(_UnderlyingType), this)

import config;
import "atomic.llvm";

[initCtor] datatype Atomic(T: Type) if T == Int {
using _IsAtomicType = true;
using ValueType = Int;

//! The value for this atomic
_value: ValueType;
}

[initCtor] datatype Atomic(T: Type) if T == Long {
using _IsAtomicType = true;
using ValueType = Long;

//! The value for this atomic
_value: ValueType;
}

datatype Atomic(T: Type) if T != Int && T != Long && sizeOf(T) <= 8 {
using _IsAtomicType = true;

using ValueType = T;

using _UnderlyingType = Impl.AtomicTypeTraits(T)._UnderlyingType;
using _BaseAtomicType = Impl.AtomicTypeTraits(T)._BaseAtomicType;

//! The value for this atomic
_value: _UnderlyingType;

}

fun ctor(this: @Atomic, v: this.ValueType)
_value construct _toUnderlying(v);

fun _toUnderlying(this: @Atomic, val: this.ValueType): this._UnderlyingType {
var res: _UnderlyingType;
reinterpretCast(@T, res) = val;
return res;
}
fun _fromUnderlying(this: @Atomic, val: this._UnderlyingType): T {
var res: ValueType;
res = reinterpretCast(@ValueType, val);
return res;
}
fun _baseAtomic(this: @Atomic): @_BaseAtomicType = reinterpretCast(@_BaseAtomicType, this);

using AtomicInt = Atomic(Int);
using AtomicLong = Atomic(Long);

concept AtomicType(x) if x._IsAtomicType;
concept AtomicInteger(x) if x._IsAtomicType && Integer(#$x.ValueType);
concept NonStdAtomicType(x) if x._IsAtomicType && x.ValueType != x._UnderlyingType;

fun =(lhs: @AtomicType, rhs: AtomicType) { lhs store (rhs load); }
//! Compare the atomic value with the given comparand; if equal store 'newVal' and return true; if not equal returns false
[native("_Atomic_compareAndSwap32")] fun compareAndSwap(x: @AtomicInt, newVal, comparand: Int): Bool;
[native("_Atomic_compareAndSwap64")] fun compareAndSwap(x: @AtomicLong, newVal, comparand: Long): Bool;
fun compareAndSwap(x: @NonStdAtomicType, newVal, comparand: AnyType): Bool \
= compareAndSwap(x._baseAtomic, x._toUnderlying(newVal), x._toUnderlying(comparand))

//! Converts a regular value to an atomic value
//! Returns the reference to the same memory address
//! Used to make it possible to use atomic operations
fun asAtomic(val: @AnyType): @Atomic(typeOf(val)) \
= reinterpretCast(@Atomic(typeOf(val)), val) \
if sizeOf(val) == sizeOf(Atomic(typeOf(val)));

//! Loads the value from an atomic; returns a non-atomic value
[native("_Atomic_load32")] fun load(x: @AtomicInt): Int;
[native("_Atomic_load64")] fun load(x: @AtomicLong): Long;
fun load(x: @NonStdAtomicType): x.ValueType \
= x._fromUnderlying(x._baseAtomic load);

//! Stores a value inside the given atomic
[native("_Atomic_store32")] fun store(x: @AtomicInt, newVal: Int);
[native("_Atomic_store64")] fun store(x: @AtomicLong, newVal: Long);
fun store(x: @NonStdAtomicType, newVal: AnyType)
{ x._baseAtomic store x._toUnderlying(newVal); }

//! Stores a value inside the given atomic, using the assignment operator
fun =(x: @AtomicType, val: AnyType) = store(x, val);

//! Fetches the current value of an atomic, and stores a new value in the atomic
[native("_Atomic_fetchAndStore32")] fun fetchAndStore(x: @AtomicInt, newVal: Int): Int;
[native("_Atomic_fetchAndStore64")] fun fetchAndStore(x: @AtomicLong, newVal: Long): Long;
fun fetchAndStore(x: @NonStdAtomicType, newVal: AnyType): x.ValueType \
= x._fromUnderlying(x._baseAtomic fetchAndStore x._toUnderlying(newVal));
fun asAtomic(val: @AnyType): @Atomic(-@typeOf(val)) if sizeOf(val) == sizeOf(Atomic(-@typeOf(val)))
return reinterpretCast(@Atomic(-@typeOf(val)), val)

//! Compare the atomic value with the given comparand; if equal store 'newVal' and return true; if not equal returns false
[native("_Atomic_compareAndSwap32")] fun compareAndSwap(x: @AtomicInt, newVal, comparand: Int): Bool;
[native("_Atomic_compareAndSwap64")] fun compareAndSwap(x: @AtomicLong, newVal, comparand: Long): Bool;
fun compareAndSwap(x: @NonStdAtomicType, newVal, comparand: AnyType): Bool \
= compareAndSwap(x._baseAtomic, x._toUnderlying(newVal), x._toUnderlying(comparand));

//! Fetch the value of the atomic, and then add the given value to it
[native("_Atomic_fetchAndAdd32")] fun fetchAndAdd(x: @AtomicInt, val: Int): Int;
[native("_Atomic_fetchAndAdd64")] fun fetchAndAdd(x: @AtomicLong, val: Long): Long;
fun fetchAndAdd(x: @NonStdAtomicType, val: AnyType): x.ValueType \
= x._fromUnderlying(x._baseAtomic fetchAndAdd x._toUnderlying(val)) \
if Integer(#$x.ValueType);

//! Fetch the value of the atomic and the increment it
fun fetchAndIncrement(x: @AtomicInteger) = x fetchAndAdd 1;
//! Fetch the value of the atomic and the decrement it
fun fetchAndDecrement(x: @AtomicInteger) = x fetchAndAdd -1;

//! Adds the given value to the atomic
fun += (x: @AtomicInteger, val: AnyType) { x fetchAndAdd val; }
//! Subtracts the given value to the atomic
fun -= (x: @AtomicInteger, val: AnyType) { x fetchAndAdd (-val); }

//! Increment the atomic value; returns the new value
fun pre_++(x: @AtomicInteger): x.ValueType = (x fetchAndIncrement)+1;
//! Increment the atomic value; returns the old value
fun post_++(x: @AtomicInteger): x.ValueType = (x fetchAndIncrement);

//! Decrement the atomic value; returns the new value
fun pre_--(x: @AtomicInteger): x.ValueType = (x fetchAndDecrement)-1;
//! Decrement the atomic value; returns the old value
fun post_--(x: @AtomicInteger): x.ValueType = (x fetchAndDecrement);

package Impl {
datatype AtomicTypeTraits(T: Type) if sizeOf(T) <= 4 {
using _UnderlyingType = Int;
using _BaseAtomicType = Atomic(Int);
}
datatype AtomicTypeTraits(T: Type) if sizeOf(T) == 8 {
using _UnderlyingType = Long;
using _BaseAtomicType = Atomic(Long);
}

fun packAtomic(x: @NonStdAtomicType): @AtomicTypeTraits(x.ValueType) \
= reinterpretCast(@AtomicTypeTraits(x.ValueType), x);

fun pack(x: @NonStdAtomicType, val: AnyType): AtomicTypeTraits(x.ValueType)._UnderlyingType {
var res: AtomicTypeTraits(x.ValueType)._UnderlyingType;
reinterpretCast(@AtomicTypeTraits(x.ValueType)._UnderlyingType, res) = val;
return res;
}
fun unpack(x: @NonStdAtomicType, val: AnyType): x.ValueType {
var res: x.ValueType;
res = reinterpretCast(@AtomicTypeTraits(x.ValueType)._UnderlyingType, val);
return res;
}
}

package _Impl
datatype AtomicTypeTraits(T: Type) if sizeOf(T) <= 4
using _UnderlyingType = Int
datatype AtomicTypeTraits(T: Type) if sizeOf(T) == 8
using _UnderlyingType = Long
35 changes: 15 additions & 20 deletions SparrowImplicitLib/par/config.spr
Original file line number Diff line number Diff line change
@@ -1,32 +1,27 @@
module par.config;
module par.config

import std.ptr;

package Impl
{
using _SC_NPROCESSORS_ONLN = 58;

[native("sysconf")] fun sysconf(name: Int): Long;
}

datatype NativeThreadHandle = Byte Ptr; // Opaque type
import std.ptr

datatype NativeThreadHandle = Byte Ptr // Opaque type

fun >>(h: NativeThreadHandle, os: @OutStream)
{
if ( h.impl.isSet )
os << mkStreamRefWrapper(h.impl.get);
os << mkStreamRefWrapper(h.impl.get)
else
os << "null";
}
os << "null"

using InvalidThreadHandle = NativeThreadHandle();
using InvalidThreadHandle = NativeThreadHandle()


//! Get the number of available logical CPU cores for our process
//! This dictates how much parallelism we have to be exploit
fun getAvailableCoresNum(): UInt {
var maxProcs: Long = Impl.sysconf(Impl._SC_NPROCESSORS_ONLN);
return ife(maxProcs<1, UInt(1), UInt(maxProcs));
fun getAvailableCoresNum(): UInt
var maxProcs: Long = _Impl.sysconf(_Impl._SC_NPROCESSORS_ONLN)
return ife(maxProcs<1, UInt(1), UInt(maxProcs))
// TODO: also consider process affinity
}

package _Impl
using _SC_NPROCESSORS_ONLN = 58

[native("sysconf")] fun sysconf(name: Int): Long

Loading

0 comments on commit 5f4134b

Please sign in to comment.