Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  ref-gvn.js

  Sprache: JAVA
 

// Tests that exercise GVN and LICM. The general pattern for these tests is to
// create values near the top of the function and then create situations where
// equivalent values are created below, including in loops.

// ref.null
{
  let numAnyref = 0;
  let numExternref = 0;

  const { test1, test2 } = wasmEvalText(`(module
    (import "" "dummy1" (func $dummy1 (param anyref)))
    (import "" "dummy2" (func $dummy2 (param externref)))
    (import "" "countAnyref" (func $countAnyref))
    (import "" "countExternref" (func $countExternref))

    (func (export "test1")
      ;; Ensure that we keep two MWasmNullConstants with two different
      ;; hierarchies at the top of the graph.
      (call $dummy1 (ref.null none))
      (call $dummy2 (ref.null noextern))

      (ref.test anyref (ref.null none))
      if
        call $countAnyref
      end

      ;; If you're not careful, this null might be GVN'd with the null from the
      ;; other hierarchy, causing bad behavior.
      (ref.test externref (ref.null noextern))
      if
        call $countExternref
      end
    )
    (func (export "test2")
      (local i32 anyref externref)

      (local.tee 1 (ref.null none))
      call $dummy1
      (local.tee 2 (ref.null noextern))
      call $dummy2

      loop
        (ref.test anyref (local.get 1))
        if
          call $countAnyref
        end

        (ref.test externref (local.get 2))
        if
          call $countExternref
        end

        (i32.ge_s (local.get 0) (i32.const 5))
        if
          ;; Note that these use the top type instead of the bottom type.
          (local.set 1 (ref.null any))
          (local.set 2 (ref.null extern))
        end

        (local.tee 0 (i32.add (local.get 0) (i32.const 1)))
        (i32.lt_s (i32.const 10))
        br_if 0
      end
    )
  )`, {
    "": {
      dummy1() {},
      dummy2() {},
      countAnyref() {
        numAnyref += 1;
      },
      countExternref() {
        numExternref += 1;
      },
    },
  }).exports;

  for (let i = 0; i < 100; i++) {
    test1();
  }

  assertEq(numAnyref, 100);
  assertEq(numExternref, 100);

  for (let i = 0; i < 100; i++) {
    test2();
  }

  assertEq(numAnyref, 1100);
  assertEq(numExternref, 1100);
}

// structs and struct fields
{
  const counts = { 1000200030004000 };

  const { test1, test2 } = wasmEvalText(`(module
    (type $a (struct (field i32)))
    (type $s (struct (field (ref $a)) (field (ref $a))))

    (import "" "dummy" (func $dummy (param anyref)))
    (import "" "count" (func $count (param i32)))

    (func (export "test1")
      (local anyref)

      (struct.new $a (i32.const 100))
      (struct.new $a (i32.const 200))
      struct.new $s
      local.tee 0
      call $dummy

      local.get 0
      ref.cast (ref $s)
      struct.get $s 0
      ref.cast (ref $a)
      struct.get $a 0
      call $count

      local.get 0
      ref.cast (ref $s)
      struct.get $s 1
      ref.cast (ref $a)
      struct.get $a 0
      call $count
    )
    (func (export "test2")
      (local anyref i32)

      (struct.new $a (i32.const 100))
      (struct.new $a (i32.const 200))
      struct.new $s
      local.tee 0
      call $dummy

      loop
        local.get 0
        ref.cast (ref $s)
        struct.get $s 0
        ref.cast (ref $a)
        struct.get $a 0
        call $count

        local.get 0
        ref.cast (ref $s)
        struct.get $s 1
        ref.cast (ref $a)
        struct.get $a 0
        call $count

        (i32.ge_s (local.get 1) (i32.const 4))
        if
          (struct.new $a (i32.const 300))
          (struct.new $a (i32.const 400))
          struct.new $s
          local.set 0
        end

        (local.tee 1 (i32.add (local.get 1) (i32.const 1)))
        (i32.lt_s (i32.const 10))
        br_if 0
      end
    )
  )`, {
    "": {
      dummy() {},
      count(n) {
        counts[n] += 1;
      },
    },
  }).exports;

  for (let i = 0; i < 100; i++) {
    test1();
  }

  assertEq(counts[100], 100);
  assertEq(counts[200], 100);
  assertEq(counts[300], 0);
  assertEq(counts[400], 0);

  for (let i = 0; i < 100; i++) {
    test2();
  }

  assertEq(counts[100], 600);
  assertEq(counts[200], 600);
  assertEq(counts[300], 500);
  assertEq(counts[400], 500);
}

// arrays and array elems
{
  const counts = { 1000200030004000 };

  const { test1, test2 } = wasmEvalText(`(module
    (type $e (array i32))
    (type $a (array (ref $e)))

    (import "" "dummy" (func $dummy (param anyref)))
    (import "" "count" (func $count (param i32)))

    (func (export "test1")
      (local anyref)

      (array.new_fixed $e 1 (i32.const 100))
      (array.new_fixed $e 1 (i32.const 200))
      array.new_fixed $a 2
      local.tee 0
      call $dummy

      local.get 0
      ref.cast (ref $a)
      (array.get $a (i32.const 0))
      ref.cast (ref $e)
      (array.get $e (i32.const 0))
      call $count

      local.get 0
      ref.cast (ref $a)
      (array.get $a (i32.const 1))
      ref.cast (ref $e)
      (array.get $e (i32.const 0))
      call $count
    )
    (func (export "test2")
      (local anyref i32)

      (array.new_fixed $e 1 (i32.const 100))
      (array.new_fixed $e 1 (i32.const 200))
      array.new_fixed $a 2
      local.tee 0
      call $dummy

      loop
        local.get 0
        ref.cast (ref $a)
        (array.get $a (i32.const 0))
        ref.cast (ref $e)
        (array.get $e (i32.const 0))
        call $count

        local.get 0
        ref.cast (ref $a)
        (array.get $a (i32.const 1))
        ref.cast (ref $e)
        (array.get $e (i32.const 0))
        call $count

        (i32.ge_s (local.get 1) (i32.const 4))
        if
          (array.new_fixed $e 1 (i32.const 300))
          (array.new_fixed $e 1 (i32.const 400))
          array.new_fixed $a 2
          local.set 0
        end

        (local.tee 1 (i32.add (local.get 1) (i32.const 1)))
        (i32.lt_s (i32.const 10))
        br_if 0
      end
    )
  )`, {
    "": {
      dummy() {},
      count(n) {
        counts[n] += 1;
      },
    },
  }).exports;

  for (let i = 0; i < 100; i++) {
    test1();
  }

  assertEq(counts[100], 100);
  assertEq(counts[200], 100);
  assertEq(counts[300], 0);
  assertEq(counts[400], 0);

  for (let i = 0; i < 100; i++) {
    test2();
  }

  assertEq(counts[100], 600);
  assertEq(counts[200], 600);
  assertEq(counts[300], 500);
  assertEq(counts[400], 500);
}

// table elems
{
  const counts = { 1000200030004000 };

  const { test1, test2 } = wasmEvalText(`(module
    (type $f (func))

    (import "" "dummy" (func $dummy (param anyref)))
    (import "" "count" (func $count (param i32)))

    (table funcref (elem (ref.func $count100) (ref.func $count200)))
    (table (ref null $f) (elem (ref.func $count300) (ref.func $count400)))

    (func $count100 (type $f)
      (call $count (i32.const 100))
    )
    (func $count200 (type $f)
      (call $count (i32.const 200))
    )
    (func $count300 (type $f)
      (call $count (i32.const 300))
    )
    (func $count400 (type $f)
      (call $count (i32.const 400))
    )

    (func (export "test1")
      (table.get 0 (i32.const 0))
      ref.cast (ref null $f)
      call_ref $f

      (table.get 0 (i32.const 1))
      ref.cast (ref null $f)
      call_ref $f

      (table.get 1 (i32.const 0))
      ref.cast (ref null $f)
      call_ref $f

      (table.get 1 (i32.const 1))
      ref.cast (ref null $f)
      call_ref $f
    )
    (func (export "test2")
      (local i32)

      (table.get 0 (i32.const 0))
      ref.cast (ref null $f)
      call_ref $f

      (table.get 1 (i32.const 0))
      ref.cast (ref null $f)
      call_ref $f

      loop
        (table.get 0 (i32.const 0))
        ref.cast (ref null $f)
        call_ref $f

        (table.get 0 (i32.const 1))
        ref.cast (ref null $f)
        call_ref $f

        (table.get 1 (i32.const 0))
        ref.cast (ref null $f)
        call_ref $f

        (table.get 1 (i32.const 1))
        ref.cast (ref null $f)
        call_ref $f

        (local.tee 0 (i32.add (local.get 0) (i32.const 1)))
        (i32.lt_s (i32.const 10))
        br_if 0
      end
    )
  )`, {
    "": {
      dummy() {},
      count(n) {
        counts[n] += 1;
      },
    },
  }).exports;

  for (let i = 0; i < 100; i++) {
    test1();
  }

  assertEq(counts[100], 100);
  assertEq(counts[200], 100);
  assertEq(counts[300], 100);
  assertEq(counts[400], 100);

  for (let i = 0; i < 100; i++) {
    test2();
  }

  assertEq(counts[100], 1200);
  assertEq(counts[200], 1100);
  assertEq(counts[300], 1200);
  assertEq(counts[400], 1100);
}

// ref.i31
{
  const countsI31 = { 100020003000 };
  const countsI32 = { 100020003000 };
  const countsU32 = { 100020003000 };

  const { test1, test2 } = wasmEvalText(`(module
    (import "" "dummy" (func $dummy (param i31ref)))
    (import "" "countI31" (func $countI31 (param i31ref)))
    (import "" "countI32" (func $countI32 (param i32)))
    (import "" "countU32" (func $countU32 (param i32)))

    (func (export "test1")
      (call $dummy (ref.i31 (i32.const 100)))
      (call $dummy (ref.i31 (i32.const 200)))

      (call $countI31 (ref.i31 (i32.const 100)))
      (call $countI31 (ref.i31 (i32.const 200)))
      (call $countI32 (i31.get_s (ref.i31 (i32.const 100))))
      (call $countI32 (i31.get_s (ref.i31 (i32.const 200))))
      (call $countU32 (i31.get_u (ref.i31 (i32.const 100))))
      (call $countU32 (i31.get_u (ref.i31 (i32.const 200))))
    )
    (func (export "test2")
      (local i32 i31ref)

      (call $dummy (ref.i31 (i32.const 100)))
      (local.tee 1 (ref.i31 (i32.const 200)))
      call $dummy

      loop
        (call $countI31 (ref.i31 (i32.const 100)))
        (call $countI31 (local.get 1))
        (call $countI32 (i31.get_s (ref.i31 (i32.const 100))))
        (call $countI32 (i31.get_s (local.get 1)))
        (call $countU32 (i31.get_u (ref.i31 (i32.const 100))))
        (call $countU32 (i31.get_u (local.get 1)))

        (local.set 1 (ref.i31 (i32.const 300)))

        (local.tee 0 (i32.add (local.get 0) (i32.const 1)))
        (i32.lt_s (i32.const 10))
        br_if 0
      end
    )
  )`, {
    "": {
      dummy() {},
      countI31(n) {
        countsI31[n] += 1;
      },
      countI32(n) {
        countsI32[n] += 1;
      },
      countU32(n) {
        countsU32[n] += 1;
      },
    },
  }).exports;

  for (let i = 0; i < 100; i++) {
    test1();
  }

  assertEq(countsI31[100], 100);
  assertEq(countsI31[200], 100);
  assertEq(countsI31[300], 0);
  assertEq(countsI32[100], 100);
  assertEq(countsI32[200], 100);
  assertEq(countsI32[300], 0);
  assertEq(countsU32[100], 100);
  assertEq(countsU32[200], 100);
  assertEq(countsU32[300], 0);

  for (let i = 0; i < 100; i++) {
    test2();
  }

  assertEq(countsI31[100], 1100);
  assertEq(countsI31[200], 200);
  assertEq(countsI31[300], 900);
  assertEq(countsI32[100], 1100);
  assertEq(countsI32[200], 200);
  assertEq(countsI32[300], 900);
  assertEq(countsU32[100], 1100);
  assertEq(countsU32[200], 200);
  assertEq(countsU32[300], 900);
}

// parameters and calls
{
  let numNull = 0;
  let numNonNull = 0;

  // Note that we are ok with inlining kicking in eventually (and have
  // constructed these tests to trigger that).
  const { test1, test2 } = wasmEvalText(`(module
    (type $f_anyref (func (param anyref)))
    (type $f_nullref (func (param nullref)))

    (import "" "thing" (global $thing externref))
    (import "" "countNull" (func $countNull))
    (import "" "countNonNull" (func $countNonNull))

    (table funcref (elem
      (ref.func $testNull_ref.is_null)
      (ref.func $mustNull_ref.is_null)
      (ref.func $testNull_ref.test)
      (ref.func $mustNull_ref.test)
    ))

    (func $makeNull_Loose (result anyref)
      ref.null any
    )
    (func $makeNonNull_Loose (result anyref)
      (any.convert_extern (global.get $thing))
    )
    (func $makeNull_Precise (result nullref)
      ref.null none
    )
    (func $makeNonNull_Precise (result (ref any))
      (ref.as_non_null (any.convert_extern (global.get $thing)))
    )

    (func $testNull_ref.is_null (param anyref)
      (ref.is_null (local.get 0))
      if
        call $countNull
      else
        call $countNonNull
      end
    )
    (func $mustNull_ref.is_null (param nullref)
      (ref.is_null (local.get 0)) ;; trivial!
      if
        call $countNull
      end
    )
    (func $testNull_ref.test (param anyref)
      (ref.test nullref (local.get 0)) ;; trivial!
      if
        call $countNull
      else
        call $countNonNull
      end
    )
    (func $mustNull_ref.test (param nullref)
      (ref.test nullref (local.get 0)) ;; trivial!
      if
        call $countNull
      end
    )

    (func (export "test1")
      (local anyref)

      (local.set 0 (call $makeNull_Loose))
      (call $testNull_ref.is_null (local.get 0))
      (call $testNull_ref.test (local.get 0))
      (call_indirect (type $f_anyref) (local.get 0) (i32.const 0))
      (call_indirect (type $f_anyref) (local.get 0) (i32.const 2))
      (ref.is_null (local.get 0)) ;; guaranteed to succeed
      if
        (call $mustNull_ref.is_null (ref.cast nullref (local.get 0)))
        (call $mustNull_ref.test (ref.cast nullref (local.get 0)))
        (call_indirect (type $f_nullref) (ref.cast nullref (local.get 0)) (i32.const 1))
        (call_indirect (type $f_nullref) (ref.cast nullref (local.get 0)) (i32.const 3))
      end

      (local.set 0 (call $makeNull_Precise))
      (call $testNull_ref.is_null (local.get 0))
      (call $testNull_ref.test (local.get 0))
      (call_indirect (type $f_anyref) (local.get 0) (i32.const 0))
      (call_indirect (type $f_anyref) (local.get 0) (i32.const 2))
      (ref.is_null (local.get 0)) ;; guaranteed to succeed
      if
        (call $mustNull_ref.is_null (ref.cast nullref (local.get 0)))
        (call $mustNull_ref.test (ref.cast nullref (local.get 0)))
        (call_indirect (type $f_nullref) (ref.cast nullref (local.get 0)) (i32.const 1))
        (call_indirect (type $f_nullref) (ref.cast nullref (local.get 0)) (i32.const 3))
      end

      (local.set 0 (call $makeNonNull_Loose))
      (call $testNull_ref.is_null (local.get 0))
      (call $testNull_ref.test (local.get 0))
      (call_indirect (type $f_anyref) (local.get 0) (i32.const 0))
      (call_indirect (type $f_anyref) (local.get 0) (i32.const 2))
      (ref.is_null (local.get 0)) ;; guaranteed to fail
      if
        (call $mustNull_ref.is_null (ref.cast nullref (local.get 0)))
        (call $mustNull_ref.test (ref.cast nullref (local.get 0)))
        (call_indirect (type $f_nullref) (ref.cast nullref (local.get 0)) (i32.const 1))
        (call_indirect (type $f_nullref) (ref.cast nullref (local.get 0)) (i32.const 3))
      end

      (local.set 0 (call $makeNonNull_Precise))
      (call $testNull_ref.is_null (local.get 0))
      (call $testNull_ref.test (local.get 0))
      (call_indirect (type $f_anyref) (local.get 0) (i32.const 0))
      (call_indirect (type $f_anyref) (local.get 0) (i32.const 2))
      (ref.is_null (local.get 0)) ;; guaranteed to fail
      if
        (call $mustNull_ref.is_null (ref.cast nullref (local.get 0)))
        (call $mustNull_ref.test (ref.cast nullref (local.get 0)))
        (call_indirect (type $f_nullref) (ref.cast nullref (local.get 0)) (i32.const 1))
        (call_indirect (type $f_nullref) (ref.cast nullref (local.get 0)) (i32.const 3))
      end
    )
    (func (export "test2")
      (local anyref i32)

      block
        loop
          (i32.eq (local.get 1) (i32.const 0))
          if
            (local.set 0 (call $makeNull_Loose))
          else
            (i32.eq (local.get 1) (i32.const 1))
            if
              (local.set 0 (call $makeNull_Precise))
            else
              (i32.eq (local.get 1) (i32.const 2))
              if
                (local.set 0 (call $makeNonNull_Loose))
              else
                (i32.eq (local.get 1) (i32.const 3))
                if
                  (local.set 0 (call $makeNonNull_Precise))
                else
                  return
                end
              end
            end
          end

          (call $testNull_ref.is_null (local.get 0))
          (call $testNull_ref.test (local.get 0))
          (call_indirect (type $f_anyref) (local.get 0) (i32.const 0))
          (call_indirect (type $f_anyref) (local.get 0) (i32.const 2))
          (ref.is_null (local.get 0))
          if
            (call $mustNull_ref.is_null (ref.cast nullref (local.get 0)))
            (call $mustNull_ref.test (ref.cast nullref (local.get 0)))
            (call_indirect (type $f_nullref) (ref.cast nullref (local.get 0)) (i32.const 1))
            (call_indirect (type $f_nullref) (ref.cast nullref (local.get 0)) (i32.const 3))
          end

          (local.set 1 (i32.add (local.get 1) (i32.const 1)))
          br 0
        end
      end
    )
  )`, {
    "": {
      "thing""hello I am extern guy",
      countNull() {
        numNull += 1;
      },
      countNonNull() {
        numNonNull += 1;
      },
    },
  }).exports;

  for (let i = 0; i < 100; i++) {
    test1();
  }

  assertEq(numNull, 1600);
  assertEq(numNonNull, 800);

  for (let i = 0; i < 100; i++) {
    test2();
  }

  assertEq(numNull, 3200);
  assertEq(numNonNull, 1600);
}

// globals
{
  const counts = { 10002000 };

  const { test1, test2 } = wasmEvalText(`(module
    (type $f (func))

    (import "" "dummy" (func $dummy (param anyref)))
    (import "" "count" (func $count (param i32)))

    (global $a (mut (ref $f)) (ref.func $count100))
    (global $b (mut (ref $f)) (ref.func $count200))

    (func $count100 (type $f)
      (call $count (i32.const 100))
    )
    (func $count200 (type $f)
      (call $count (i32.const 200))
    )

    (func (export "test1")
      global.get $a
      call_ref $f

      global.get $b
      call_ref $f
    )
    (func (export "test2")
      (local funcref i32)

      (local.set 0 (global.get $a))

      loop
        local.get 0
        ref.cast (ref $f)
        call_ref $f

        (i32.ge_s (local.get 1) (i32.const 4))
        if
          (local.set 0 (global.get $b))
        end

        (local.tee 1 (i32.add (local.get 1) (i32.const 1)))
        (i32.lt_s (i32.const 10))
        br_if 0
      end
    )
  )`, {
    "": {
      dummy() {},
      count(n) {
        counts[n] += 1;
      },
    },
  }).exports;

  for (let i = 0; i < 100; i++) {
    test1();
  }

  assertEq(counts[100], 100);
  assertEq(counts[200], 100);

  for (let i = 0; i < 100; i++) {
    test2();
  }

  assertEq(counts[100], 600);
  assertEq(counts[200], 600);
}

// any.convert_extern and extern.convert_any
{
  let numAnyref = 0;
  let numExternref = 0;

  const { test1, test2 } = wasmEvalText(`(module
    (import "" "thing" (global $extern1 externref))
    (import "" "thing" (global $extern2 externref))
    (import "" "dummy1" (func $dummy1 (param anyref)))
    (import "" "dummy2" (func $dummy2 (param externref)))
    (import "" "countAnyref" (func $countAnyref))
    (import "" "countExternref" (func $countExternref))

    (global $any1 anyref (any.convert_extern (global.get $extern1)))
    (global $any2 anyref (any.convert_extern (global.get $extern2)))

    (func (export "test1")
      (call $dummy1 (any.convert_extern (global.get $extern1)))
      (call $dummy2 (extern.convert_any (global.get $any1)))

      ;; These tests are trivial.

      (ref.test anyref (any.convert_extern (global.get $extern1)))
      if
        call $countAnyref
      end

      (ref.test externref (extern.convert_any (global.get $any1)))
      if
        call $countExternref
      end
    )
    (func (export "test2")
      (local i32 anyref externref)

      (local.tee 1 (any.convert_extern (global.get $extern1)))
      call $dummy1
      (local.tee 2 (extern.convert_any (global.get $any1)))
      call $dummy2

      loop
        ;; These tests are again trivial.

        (ref.test anyref (local.get 1))
        if
          call $countAnyref
        end

        (ref.test externref (local.get 2))
        if
          call $countExternref
        end

        (i32.ge_s (local.get 0) (i32.const 5))
        if
          ;; These are different values but have the same type. It will not
          ;; affect the types that flow into the tests.
          (local.set 1 (any.convert_extern (global.get $extern2)))
          (local.set 2 (extern.convert_any (global.get $any2)))
        end

        (local.tee 0 (i32.add (local.get 0) (i32.const 1)))
        (i32.lt_s (i32.const 10))
        br_if 0
      end
    )
  )`, {
    "": {
      "thing""hello I am extern guy",
      dummy1() {},
      dummy2() {},
      countAnyref() {
        numAnyref += 1;
      },
      countExternref() {
        numExternref += 1;
      },
    },
  }).exports;

  for (let i = 0; i < 100; i++) {
    test1();
  }

  assertEq(numAnyref, 100);
  assertEq(numExternref, 100);

  for (let i = 0; i < 100; i++) {
    test2();
  }

  assertEq(numAnyref, 1100);
  assertEq(numExternref, 1100);
}

// Test uses of ref.null, which always produces nodes of type (ref null none)
{
  new WebAssembly.Module(wasmTextToBinary(`(module
    (type $s1 (struct (field anyref)))
    (type $s2 (struct (field i31ref)))
    (type $s3 (struct (field structref)))
    (func
      ref.null $s1
      struct.get $s1 0
      drop

      ref.null $s2
      struct.get $s2 0
      drop
    )
    (func
      ref.null $s2
      struct.get $s2 0
      drop

      ref.null $s3
      struct.get $s3 0
      drop
    )
    (func
      ref.null $s1
      struct.get $s1 0
      drop

      ref.null $s2
      struct.get $s2 0
      drop

      ref.null $s3
      struct.get $s3 0
      drop
    )
  )`));
}
{
  const { test0, test1 } = wasmEvalText(`(module
    (type $s (struct (field i32)))
    (func (export "test0") (param i32) (result anyref anyref)
      local.get 0
      if (result anyref anyref)
        ref.null any
        (select (result anyref) (struct.new $s (i32.const 1337)) (ref.null any) (i32.const 0))
      else
        ref.null any
        ref.null any
      end
    )
    (func (export "test1") (param i32) (result anyref anyref)
      local.get 0
      if (result anyref anyref)
        ref.null any
        (select (result anyref) (struct.new $s (i32.const 1337)) (ref.null any) (i32.const 1))
      else
        ref.null any
        ref.null any
      end
    )
  )`).exports;
  const [a00, b00] = test0(0);
  assertEq(a00, null);
  assertEq(b00, null);
  const [a01, b01] = test0(1);
  assertEq(a01, null);
  assertEq(b01, null);
  const [a10, b10] = test1(0);
  assertEq(a10, null);
  assertEq(b10, null);
  const [a11, b11] = test1(1);
  assertEq(a11, null);
  assertEq(wasmGcReadField(b11, 0), 1337);
}
{
  wasmEvalText(`(module
    (func
      ref.null any
      ref.as_non_null
      drop

      ref.null eq
      ref.as_non_null
      drop
    )
  )`);
}

if (getBuildConfiguration("jitspew")) {
  // Test type calculations from phis (uses LUB to compute common type for all values)
  {
    const code = wasmTextToBinary(`(module
      (type $s1 (struct (field i32)))
      (type $s2 (struct (field i64)))
      (func (param i32) (result structref)
        (local $v anyref)

        local.get 0
        if
          (struct.new $s1 (i32.const 1))
          local.set $v
        else
          (struct.new $s2 (i64.const 1))
          local.set $v
        end

        ;; Because two different values are pulled together into one phi, the LUB
        ;; will be used to compute a value for the phi, which should be
        ;; (ref struct).

        ;; This test will fold to a constant 0 and the branch will be eliminated.
        local.get $v
        ref.test arrayref
        if
          unreachable
        end

        ;; This cast is trivial and will be eliminated.
        local.get $v
        ref.cast (ref struct)

        ;; However, this cast is not trivial and must be preserved.
        ref.cast (ref $s1)
      )
    )`);
    const ionJSON = wasmGetIon(code, 0);
    const unoptimized = wasmIonGetFirstMIRPass(ionJSON);
    const optimized = wasmIonGetLastMIRPass(ionJSON);

    assertOpcodesInOrder(unoptimized, [
      "WasmNewStructObject""WasmNewStructObject",
      "Phi",
      "WasmRefTestAbstract",
      "WasmTrap",
      "WasmRefCastAbstract",
      "WasmRefCastConcrete",
    ]);
    assertOpcodesInOrder(optimized, [
      "WasmNewStructObject""WasmNewStructObject",
      "Phi",
      "!WasmRefTestAbstract",
      "!WasmTrap",
      "!WasmRefCastAbstract",
      "WasmRefCastConcrete",
    ]);
  }

  // Test cases of tests/casts dominating tests/casts for the same value.
  {
    // test -> cast
    const code = wasmTextToBinary(`(module
      (type $s (struct (field i32)))
      (func (export "test") (param anyref) (result i32)
        local.get 0
        ref.test (ref null $s)
        if
          local.get 0
          ref.cast (ref null $s)
          struct.get $s 0
          return
        end
        i32.const -1
      )
      (func (export "make") (result anyref)
        (struct.new $s (i32.const 123))
      )
    )`);
    const ionJSON = wasmGetIon(code, 0);
    const unoptimized = wasmIonGetFirstMIRPass(ionJSON);
    const optimized = wasmIonGetLastMIRPass(ionJSON);

    assertOpcodesInOrder(unoptimized, ["WasmRefTestConcrete""WasmRefCastConcrete"]);
    assertOpcodesInOrder(optimized, ["WasmRefTestConcrete""WasmRefCastInfallible"]);

    const { test, make } = wasmEvalBinary(code).exports;
    assertEq(test(make()), 123);
  }
  {
    // test -> test
    const code = wasmTextToBinary(`(module
      (type $s (struct (field i32)))
      (func (export "test") (param anyref) (result i32)
        local.get 0
        ref.test (ref $s)
        if
          local.get 0
          ref.test (ref $s) ;; known to succeed
          if
            local.get 0
            ref.cast (ref null $s) ;; note: wider type than the test
            struct.get $s 0
            return
          end
        else
          local.get 0
          ref.test (ref $s) ;; known to fail
          if
            unreachable
          end
        end
        i32.const -1
      )
      (func (export "make") (result anyref)
        (struct.new $s (i32.const 123))
      )
    )`);
    const ionJSON = wasmGetIon(code, 0);
    const unoptimized = wasmIonGetFirstMIRPass(ionJSON);
    const optimized = wasmIonGetLastMIRPass(ionJSON);

    assertOpcodesInOrder(unoptimized, [
      "WasmRefTestConcrete",
      "WasmRefTestConcrete",
      "WasmRefCastConcrete",
      "WasmRefTestConcrete",
      "WasmTrap",
      "WasmReturn",
    ]);
    assertOpcodesInOrder(optimized, [
      "WasmRefTestConcrete",
      "!WasmRefTestConcrete",
      "!WasmRefCastConcrete""WasmRefCastInfallible",
      "!WasmRefTestConcrete",
      "!WasmTrap",
      "WasmReturn",
    ]);

    const { test, make } = wasmEvalBinary(code).exports;
    assertEq(test(make()), 123);
  }
  {
    // cast -> cast
    const code = wasmTextToBinary(`(module
      (type $s (struct (field i32)))
      (func (export "test") (param anyref) (result i32)
        local.get 0
        ref.cast (ref $s)
        struct.get $s 0
        if
          local.get 0
          ref.cast (ref null $s) ;; note: wider type than the previous cast
          struct.get $s 0
          return
        end
        i32.const -1
      )
      (func (export "make") (result anyref)
        (struct.new $s (i32.const 123))
      )
    )`);
    const ionJSON = wasmGetIon(code, 0);
    const unoptimized = wasmIonGetFirstMIRPass(ionJSON);
    const optimized = wasmIonGetLastMIRPass(ionJSON);

    assertOpcodesInOrder(unoptimized, ["WasmRefCastConcrete""WasmRefCastConcrete"]);
    assertOpcodesInOrder(optimized, ["WasmRefCastConcrete""!WasmRefCastConcrete""!WasmRefCastInfallible"]);

    const { test, make } = wasmEvalBinary(code).exports;
    assertEq(test(make()), 123);
  }
  {
    // cast -> test
    const code = wasmTextToBinary(`(module
      (type $s (struct (field i32)))
      (func (export "test") (param anyref) (result i32)
        local.get 0
        ref.cast (ref $s)
        struct.get $s 0
        if
          local.get 0
          ref.test (ref null $s) ;; note: wider type than the cast
          if
            local.get 0
            ref.cast (ref $s)
            struct.get $s 0
            return
          end
        end
        i32.const -1
      )
      (func (export "make") (result anyref)
        (struct.new $s (i32.const 123))
      )
    )`);
    const ionJSON = wasmGetIon(code, 0);
    const unoptimized = wasmIonGetFirstMIRPass(ionJSON);
    const optimized = wasmIonGetLastMIRPass(ionJSON);

    assertOpcodesInOrder(unoptimized, ["WasmRefCastConcrete""WasmRefTestConcrete""WasmRefCastConcrete"]);
    assertOpcodesInOrder(optimized, ["WasmRefCastConcrete""!WasmRefTestConcrete""!WasmRefCastConcrete""!WasmRefCastInfallible"]);

    const { test, make } = wasmEvalBinary(code).exports;
    assertEq(test(make()), 123);
  }
  {
    // ref.is_null is emitted as `ref.test nullref`, so it should participate in
    // the same kinds of optimizations.
    const code = wasmTextToBinary(`(module
      (type $s (struct (field i32)))
      (func (export "test") (param anyref) (result i32)
        local.get 0
        ref.is_null
        if
          local.get 0
          ref.cast nullref ;; trivial, will disappear
          struct.get $s 0
          return
        else
          local.get 0
          ref.is_null ;; known to fail
          if
            unreachable
          end
        end
        i32.const -1
      )
      (func (export "make") (result anyref)
        (struct.new $s (i32.const 123))
      )
    )`);
    const ionJSON = wasmGetIon(code, 0);
    const unoptimized = wasmIonGetFirstMIRPass(ionJSON);
    const optimized = wasmIonGetLastMIRPass(ionJSON);

    assertOpcodesInOrder(unoptimized, [
      "WasmRefTestAbstract",
      "WasmRefCastAbstract",
      "WasmRefTestAbstract",
      "WasmTrap",
      "WasmReturn",
    ]);
    assertOpcodesInOrder(optimized, [
      "WasmRefTestAbstract",
      "!WasmRefCastAbstract",
      "!WasmRefTestAbstract",
      "!WasmTrap",
      "WasmReturn",
    ]);

    const { test, make } = wasmEvalBinary(code).exports;
    assertEq(test(make()), -1);
  }
}

Messung V0.5 in Prozent
C=99 H=90 G=94

¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002