Skip to content
Snippets Groups Projects
simdUtilities.js 11.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • // Copyright (C) 2016 ecmascript_simd authors. All rights reserved.
    // This code is governed by the BSD license found in the LICENSE file.
    
    function minNum(x, y) {
      return x != x ? y :
    
        y != y ? x :
        Math.min(x, y);
    
    }
    
    function maxNum(x, y) {
      return x != x ? y :
    
        y != y ? x :
        Math.max(x, y);
    
    }
    
    function sameValue(x, y) {
      if (x == y)
        return x != 0 || y != 0 || (1/x == 1/y);
    
      return x != x && y != y;
    }
    
    function binaryMul(a, b) { return a * b; }
    var binaryImul;
    if (typeof Math.imul !== "undefined") {
      binaryImul = Math.imul;
    } else {
      binaryImul = function(a, b) {
    
      var ah = (a >>> 16) & 0xffff;
      var al = a & 0xffff;
      var bh = (b >>> 16) & 0xffff;
      var bl = b & 0xffff;
      // the shift by 0 fixes the sign on the high part
      // the final |0 converts the unsigned value into a signed value
      return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0)|0);
    
      };
    }
    
    var _f32x4 = new Float32Array(4);
    var _f64x2 = new Float64Array(_f32x4.buffer);
    var _i32x4 = new Int32Array(_f32x4.buffer);
    var _i16x8 = new Int16Array(_f32x4.buffer);
    var _i8x16 = new Int8Array(_f32x4.buffer);
    var _ui32x4 = new Uint32Array(_f32x4.buffer);
    var _ui16x8 = new Uint16Array(_f32x4.buffer);
    var _ui8x16 = new Uint8Array(_f32x4.buffer);
    
    var float32x4 = {
      name: "Float32x4",
      fn: SIMD.Float32x4,
      floatLane: true,
      signed: true,
      numerical: true,
      lanes: 4,
      laneSize: 4,
      interestingValues: [0, -0, 1, -1, 0.9, -0.9, 1.414, 0x7F, -0x80, -0x8000,
      -0x80000000, 0x7FFF, 0x7FFFFFFF, Infinity, -Infinity, NaN],
      view: Float32Array,
      buffer: _f32x4,
      mulFn: binaryMul,
    }
    
    var int32x4 = {
      name: "Int32x4",
      fn: SIMD.Int32x4,
      intLane: true,
      signed: true,
      numerical: true,
      logical: true,
      lanes: 4,
      laneSize: 4,
      minVal: -0x80000000,
      maxVal: 0x7FFFFFFF,
      interestingValues: [0, 1, -1, 0x40000000, 0x7FFFFFFF, -0x80000000],
      view: Int32Array,
      buffer: _i32x4,
      mulFn: binaryImul,
    }
    
    var int16x8 = {
      name: "Int16x8",
      fn: SIMD.Int16x8,
      intLane: true,
      signed: true,
      numerical: true,
      logical: true,
      lanes: 8,
      laneSize: 2,
      laneMask: 0xFFFF,
      minVal: -0x8000,
      maxVal: 0x7FFF,
      interestingValues: [0, 1, -1, 0x4000, 0x7FFF, -0x8000],
      view: Int16Array,
      buffer: _i16x8,
      mulFn: binaryMul,
    }
    
    var int8x16 = {
      name: "Int8x16",
      fn: SIMD.Int8x16,
      intLane: true,
      signed: true,
      numerical: true,
      logical: true,
      lanes: 16,
      laneSize: 1,
      laneMask: 0xFF,
      minVal: -0x80,
      maxVal: 0x7F,
      interestingValues: [0, 1, -1, 0x40, 0x7F, -0x80],
      view: Int8Array,
      buffer: _i8x16,
      mulFn: binaryMul,
    }
    
    var uint32x4 = {
      name: "Uint32x4",
      fn: SIMD.Uint32x4,
      intLane: true,
      unsigned: true,
      numerical: true,
      logical: true,
      lanes: 4,
      laneSize: 4,
      minVal: 0,
      maxVal: 0xFFFFFFFF,
      interestingValues: [0, 1, 0x40000000, 0x7FFFFFFF, 0xFFFFFFFF],
      view: Uint32Array,
      buffer: _ui32x4,
      mulFn: binaryImul,
    }
    
    var uint16x8 = {
      name: "Uint16x8",
      fn: SIMD.Uint16x8,
      intLane: true,
      unsigned: true,
      numerical: true,
      logical: true,
      lanes: 8,
      laneSize: 2,
      laneMask: 0xFFFF,
      minVal: 0,
      maxVal: 0xFFFF,
      interestingValues: [0, 1, 0x4000, 0x7FFF, 0xFFFF],
      view: Uint16Array,
      buffer: _ui16x8,
      mulFn: binaryMul,
    }
    
    var uint8x16 = {
      name: "Uint8x16",
      fn: SIMD.Uint8x16,
      intLane: true,
      unsigned: true,
      numerical: true,
      logical: true,
      lanes: 16,
      laneSize: 1,
      laneMask: 0xFF,
      minVal: 0,
      maxVal: 0xFF,
      interestingValues: [0, 1, 0x40, 0x7F, 0xFF],
      view: Int8Array,
      buffer: _ui8x16,
      mulFn: binaryMul,
    }
    
    var bool32x4 = {
      name: "Bool32x4",
      fn: SIMD.Bool32x4,
      boolLane: true,
      logical: true,
      lanes: 4,
      laneSize: 4,
      interestingValues: [true, false],
    }
    
    var bool16x8 = {
      name: "Bool16x8",
      fn: SIMD.Bool16x8,
      boolLane: true,
      logical: true,
      lanes: 8,
      laneSize: 2,
      interestingValues: [true, false],
    }
    
    var bool8x16 = {
      name: "Bool8x16",
      fn: SIMD.Bool8x16,
      boolLane: true,
      logical: true,
      lanes: 16,
      laneSize: 1,
      interestingValues: [true, false],
    }
    
    // Filter functions.
    function isFloatType(type) { return type.floatLane; }
    function isIntType(type) { return type.intLane; }
    function isBoolType(type) { return type.boolLane; }
    function isNumerical(type) { return type.numerical; }
    function isLogical(type) { return type.logical; }
    function isSigned(type) { return type.signed; }
    function isSignedIntType(type) { return type.intLane && type.signed; }
    function isUnsignedIntType(type) { return type.intLane && type.unsigned; }
    function isSmallIntType(type) { return type.intLane && type.lanes >= 8; }
    function isSmallUnsignedIntType(type) {
      return type.intLane && type.unsigned && type.lanes >= 8;
    }
    function hasLoadStore123(type) { return !type.boolLane && type.lanes == 4; }
    
    // Each SIMD type has a corresponding Boolean SIMD type, which is returned by
    // relational ops.
    float32x4.boolType = int32x4.boolType = uint32x4.boolType = bool32x4;
    int16x8.boolType = uint16x8.boolType = bool16x8;
    int8x16.boolType = uint8x16.boolType = bool8x16;
    
    // SIMD fromTIMD types.
    float32x4.from = [int32x4, uint32x4];
    int32x4.from = [float32x4];
    int16x8.from = [];
    int8x16.from = [];
    uint32x4.from = [float32x4];
    uint16x8.from = [int16x8];
    uint8x16.from = [int8x16];
    
    // SIMD fromBits types.
    float32x4.fromBits = [int32x4, int16x8, int8x16, uint32x4, uint16x8, uint8x16];
    int32x4.fromBits = [float32x4, int16x8, int8x16, uint32x4, uint16x8, uint8x16];
    int16x8.fromBits = [float32x4, int32x4, int8x16, uint32x4, uint16x8, uint8x16];
    int8x16.fromBits = [float32x4, int32x4, int16x8, uint32x4, uint16x8, uint8x16];
    uint32x4.fromBits = [float32x4, int32x4, int16x8, int8x16, uint16x8, uint8x16];
    uint16x8.fromBits = [float32x4, int32x4, int16x8, int8x16, uint32x4, uint8x16];
    uint8x16.fromBits = [float32x4, int32x4, int16x8, int8x16, uint32x4, uint16x8];
    
    var simdTypes = [float32x4,
    
      int32x4, int16x8, int8x16,
      uint32x4, uint16x8, uint8x16,
      bool32x4, bool16x8, bool8x16];
    
    
    if (typeof simdPhase2 !== "undefined") {
      var float64x2 = {
        name: "Float64x2",
        fn: SIMD.Float64x2,
        floatLane: true,
        signed: true,
        numerical: true,
        lanes: 2,
        laneSize: 8,
        interestingValues: [0, -0, 1, -1, 1.414, 0x7F, -0x80, -0x8000, -0x80000000,
        0x7FFF, 0x7FFFFFFF, Infinity, -Infinity, NaN],
        view: Float64Array,
        buffer: _f64x2,
        mulFn: binaryMul,
    
    
      var bool64x2 = {
        name: "Bool64x2",
        fn: SIMD.Bool64x2,
        boolLane: true,
        lanes: 2,
        laneSize: 8,
        interestingValues: [true, false],
    
    
      float64x2.boolType = bool64x2;
    
      float32x4.fromBits.push(float64x2);
      int32x4.fromBits.push(float64x2);
      int16x8.fromBits.push(float64x2);
      int8x16.fromBits.push(float64x2);
      uint32x4.fromBits.push(float64x2);
      uint16x8.fromBits.push(float64x2);
      uint8x16.fromBits.push(float64x2);
    
      float64x2.fromBits = [float32x4, int32x4, int16x8, int8x16,
      uint32x4, uint16x8, uint8x16];
    
      int32x4.fromBits = [float32x4, int16x8, int8x16, uint32x4,
      uint16x8, uint8x16];
      int16x8.fromBits = [float32x4, int32x4, int8x16, uint32x4,
      uint16x8, uint8x16];
      int8x16.fromBits = [float32x4, int32x4, int16x8, uint32x4,
      uint16x8, uint8x16];
      uint32x4.fromBits = [float32x4, int32x4, int16x8, int8x16,
      uint16x8, uint8x16];
      uint16x8.fromBits = [float32x4, int32x4, int16x8, int8x16,
      uint32x4, uint8x16];
      uint8x16.fromBits = [float32x4, int32x4, int16x8, int8x16,
      uint32x4, uint16x8];
    
      simdTypes.push(float64x2);
      simdTypes.push(bool64x2);
    }
    
    // SIMD utility functions.
    
    // Create a value for testing, with vanilla lane values, i.e. [0, 1, 2, ..]
    // for numeric types, [false, true, true, ..] for boolean types. These test
    // values shouldn't contain NaNs or other "interesting" values.
    function createTestValue(type) {
      var lanes = [];
      for (var i = 0; i < type.lanes; i++)
        lanes.push(i);
      return type.fn.apply(type.fn, lanes);
    }
    
    function createSplatValue(type, v) {
      var lanes = [];
      for (var i = 0; i < type.lanes; i++)
        lanes.push(v);
      return type.fn.apply(type.fn, lanes);
    }
    
    // SIMD reference functions.
    
    // Returns converted array buffer value of specified type.
    function simdConvert(type, value) {
      if (type.buffer === undefined) return !!value;  // bool types
      type.buffer[0] = value;
      return type.buffer[0];
    }
    
    function checkValue(type, a, expect) {
      var fail = false;
      for (var i = 0; i < type.lanes; i++) {
        var v = type.fn.extractLane(a, i);
        var ev = simdConvert(type, expect(i));
        if (!sameValue(ev, v) && Math.abs(ev - v) >= 0.00001)
          fail = true;
      }
      if (fail) {
    
      var lanes = [];
      for (var i = 0; i < type.lanes; i++){
        lanes.push(simdConvert(type, expect(i)));
      }
      $ERROR("expected SIMD." + type.name + "(" + lanes +
        ") but found " + a.toString());
    
      }
    }
    
    // SIMD reference functions.
    
    // Reference implementation of toLocaleString.
    function simdToLocaleString(type, value) {
      value = type.fn.check(value);
      var str = "SIMD." + type.name + "(";
      str += type.fn.extractLane(value, 0).toLocaleString();
      for (var i = 1; i < type.lanes; i++) {
        str += "," + type.fn.extractLane(value, i).toLocaleString();
      }
      return str + ")";
    }
    
    function equalInt32x4(a, b) {
      assert.sameValue(SIMD.Int32x4.extractLane(a, 0),
    
        SIMD.Int32x4.extractLane(b, 0));
    
      assert.sameValue(SIMD.Int32x4.extractLane(a, 1),
    
        SIMD.Int32x4.extractLane(b, 1));
    
      assert.sameValue(SIMD.Int32x4.extractLane(a, 2),
    
        SIMD.Int32x4.extractLane(b, 2));
    
      assert.sameValue(SIMD.Int32x4.extractLane(a, 3),
    
        SIMD.Int32x4.extractLane(b, 3));
    
    }
    
    // Compare unary op's behavior to ref op at each lane.
    function testUnaryOp(type, op, refOp) {
      assert.sameValue("function", typeof type.fn[op]);
      for (var v of type.interestingValues) {
        var expected = simdConvert(type, refOp(v));
        var a = type.fn.splat(v);
        var result = type.fn[op](a);
        checkValue(type, result, function(index) { return expected; });
      }
    }
    
    // Compare binary op's behavior to ref op at each lane with the Cartesian
    // product of the given values.
    function testBinaryOp(type, op, refOp) {
      assert.sameValue("function", typeof type.fn[op]);
      var zero = type.fn();
      for (var av of type.interestingValues) {
        for (var bv of type.interestingValues) {
          var expected = simdConvert(type, refOp(simdConvert(type, av),
    
            simdConvert(type, bv)));
    
          var a = type.fn.splat(av);
          var b = type.fn.splat(bv);
          var result = type.fn[op](a, b);
          checkValue(type, result, function(index) { return expected; });
        }
      }
    }
    
    // Compare relational op's behavior to ref op at each lane with the Cartesian
    // product of the given values.
    function testRelationalOp(type, op, refOp) {
      assert.sameValue("function", typeof type.fn[op]);
      var zero = type.fn();
      for (var av of type.interestingValues) {
        for (var bv of type.interestingValues) {
          var expected = refOp(simdConvert(type, av), simdConvert(type, bv));
          var a = type.fn.splat(av);
          var b = type.fn.splat(bv);
          var result = type.fn[op](a, b);
          checkValue(type.boolType, result, function(index) { return expected; });
        }
      }
    }
    
    // Test utilities.
    var currentName = "<global>";
    var skipValueTests = false;
    
    function testSimdFunction(name, func) {
      currentName = name;
      if (typeof skipValueTests !== "undefined" && skipValueTests &&
    
        name.indexOf("value semantics") != -1) return;
    
      try {
        func();
      } catch (e) {
        e.message += " (Testing with " + name + ".)";
        throw e;
      }
    }