Skip to content
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

Add executions tests for unary indirection #3304

Merged

Conversation

amaiorano
Copy link
Member

@amaiorano amaiorano commented Jan 23, 2024

Also tests deref followed by index/member access, including pointer_composite_access syntax if supported.
Issue: #3225


Requirements for PR author:

  • All missing test coverage is tracked with "TODO" or .unimplemented().
  • New helpers are /** documented */ and new helper files are found in helper_index.txt.
  • Test behaves as expected in a WebGPU implementation. (If not passing, explain above.)

Requirements for reviewer sign-off:

  • Tests are properly located in the test tree.
  • Test descriptions allow a reader to "read only the test plans and evaluate coverage completeness", and accurately reflect the test code.
  • Tests provide complete coverage (including validation control cases). Missing coverage MUST be covered by TODOs.
  • Helpers and types promote readability and maintainability.

When landing this PR, be sure to make any necessary issue status updates.

Copy link
Contributor

@ben-clayton ben-clayton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests look good. Some suggestions to reduce the verbosity / improve readability.


// All the ways to deref an expression
const kDerefTypes = [
'DerefAddressOfIdentifier', // *(&a)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: The WGSL spec uses the term indirection. Maybe Indir

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept trying to rename things, but in the end, I think I'm going to keep "deref" because this is the name of the operation: we "dereference" pointers, we don't "indirect" them.


// All the ways to deref an expression
const kDerefTypes = [
'DerefAddressOfIdentifier', // *(&a)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: most strings that end up in case parameters are snake_case

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}

// Returns deref expression for identifier 'a' or pointer 'p' based on deref type
function deref(derefType: DerefType): string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turn kDerefTypes into a map? The values can hold these helpers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how to do that, and to define a type like:

type DerefType = (typeof kDerefTypes)[number];

Also, I somewhat prefer keeping is as an enum-like.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant:

export const description = `
Execution Tests for unary indirection (dereference)
`;

import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { keysOf } from '../../../../../common/util/data_tables.js';
import { GPUTest } from '../../../../gpu_test.js';
import { ScalarKind, scalarType } from '../../../../util/conversion.js';
import { sparseScalarF32Range } from '../../../../util/math.js';
import {
  allButConstInputSource,
  basicExpressionWithPredeclarationBuilder,
  run,
} from '../expression.js';

export const g = makeTestGroup(GPUTest);

// All the ways to deref an expression
const kDerefCases = {
  'deref_address_of_identifier': {
    wgsl: '(*(&a))',
    requires_pointer_composite_access: false,
  },
  'deref_pointer': {
    wgsl: '(*p)',
    requires_pointer_composite_access: false,
  },
  'address_of_identifier': {
    wgsl: '(&a)',
    requires_pointer_composite_access: true,
  },
  'pointer': {
    wgsl: 'p',
    requires_pointer_composite_access: true,
  },
};

g.test('deref')
  .specURL('https://www.w3.org/TR/WGSL/#indirection')
  .desc(
    `
Expression: *e

Pointer expression dereference.
`
  )
  .params(u =>
    u
      .combine('inputSource', allButConstInputSource)
      .combine('vectorize', [undefined, 2, 3, 4] as const)
      .combine('scalarType', ['u32', 'i32', 'f32'] as ScalarKind[])
      .combine('derefType', keysOf(kDerefCases))
      .filter(p => !kDerefCases[p.derefType].requires_pointer_composite_access)
  )
  .fn(async t => {
    const ty = scalarType(t.params.scalarType);
    const cases = sparseScalarF32Range().map(e => {
      return { input: ty.create(e), expected: ty.create(e) };
    });
    const elemType = ty.kind;
    const type = t.params.vectorize ? `vec${t.params.vectorize}<${elemType}>` : elemType;
    const shaderBuilder = basicExpressionWithPredeclarationBuilder(
      value => `get_dereferenced_value(${value})`,
      `fn get_dereferenced_value(value: ${type}) -> ${type} {
        var a = value;
        let p = &a;
        return ${kDerefCases[t.params.derefType].wgsl};
      }`
    );
    await run(t, shaderBuilder, [ty], ty, t.params, cases);
  });

g.test('deref_index')
  .specURL('https://www.w3.org/TR/WGSL/#logical-expr')
  .desc(
    `
Expression: (*e)[index]

Pointer expression dereference as lhs of index accessor expression
`
  )
  .params(u =>
    u
      .combine('inputSource', allButConstInputSource)
      .combine('vectorize', [undefined, 2, 3, 4] as const)
      .combine('scalarType', ['i32', 'f32'] as ScalarKind[])
      .combine('derefType', keysOf(kDerefCases))
  )
  .fn(async t => {
    if (
      kDerefCases[t.params.derefType].requires_pointer_composite_access &&
      !t.hasLanguageFeature('pointer_composite_access')
    ) {
      return;
    }

    const ty = scalarType(t.params.scalarType);
    const cases = sparseScalarF32Range().map(e => {
      return { input: ty.create(e), expected: ty.create(e) };
    });
    const elemType = ty.kind;
    const type = t.params.vectorize ? `vec${t.params.vectorize}<${elemType}>` : elemType;
    const shaderBuilder = basicExpressionWithPredeclarationBuilder(
      value => `get_dereferenced_value(${value})`,
      `fn get_dereferenced_value(value: ${type}) -> ${type} {
        var a = array<${type}, 1>(value);
        let p = &a;
        return ${kDerefCases[t.params.derefType].wgsl}[0];
      }`
    );
    await run(t, shaderBuilder, [ty], ty, t.params, cases);
  });

g.test('deref_member')
  .specURL('https://www.w3.org/TR/WGSL/#logical-expr')
  .desc(
    `
Expression: (*e).member

Pointer expression dereference as lhs of member accessor expression
`
  )
  .params(u =>
    u
      .combine('inputSource', allButConstInputSource)
      .combine('vectorize', [undefined, 2, 3, 4] as const)
      .combine('scalarType', ['i32', 'f32'] as ScalarKind[])
      .combine('derefType', keysOf(kDerefCases))
  )
  .fn(async t => {
    if (
      kDerefCases[t.params.derefType].requires_pointer_composite_access &&
      !t.hasLanguageFeature('pointer_composite_access')
    ) {
      return;
    }

    const ty = scalarType(t.params.scalarType);
    const cases = sparseScalarF32Range().map(e => {
      return { input: ty.create(e), expected: ty.create(e) };
    });
    const elemType = ty.kind;
    const type = t.params.vectorize ? `vec${t.params.vectorize}<${elemType}>` : elemType;
    const shaderBuilder = basicExpressionWithPredeclarationBuilder(
      value => `get_dereferenced_value(${value})`,
      `struct S {
        m : ${type}
      }
      fn get_dereferenced_value(value: ${type}) -> ${type} {
        var a = S(value);
        let p = &a;
        return ${kDerefCases[t.params.derefType].wgsl}.m;
      }`
    );
    await run(t, shaderBuilder, [ty], ty, t.params, cases);
  });

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that! Done.

);
}

function derefIndexBuilder(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These guys are only used once. Suggest inlining into the place of usage.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@amaiorano amaiorano force-pushed the add-unary-indirection-execution-tests branch 2 times, most recently from b4e4e28 to de84b48 Compare January 25, 2024 19:32
Copy link
Contributor

@ben-clayton ben-clayton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Also tests deref followed by index/member access, including
pointer_composite_access syntax if supported.
@amaiorano amaiorano force-pushed the add-unary-indirection-execution-tests branch from dec78de to 6fcf053 Compare January 25, 2024 20:05
@amaiorano amaiorano merged commit 2f6a77f into gpuweb:main Jan 25, 2024
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants