mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 07:04:04 -05:00
## Summary
These tests document the expected functional behavior of AsmParser
subclasses before refactoring work begins, focusing on public API
behavior rather than implementation details.
## Changes
**Test Coverage Added:**
- **VcAsmParser**: Tests VC assembly format with PROC/ENDP structure,
directive handling, comment filtering
- **AsmEWAVRParser**: Tests EWAVR assembly format with label structure,
RSEG/PUBLIC/EXTERN directives
- **SPIRVAsmParser**: Tests SPIR-V assembly with OpFunction/OpLabel
structure, custom %label syntax detection
- **Integration tests**: Cross-parser compatibility and real test case
handling
**Key Insights Documented:**
- VcAsmParser & AsmEWAVRParser have completely custom `processAsm()`
implementations that don't use base class methods
- SPIRVAsmParser uses base class `processAsm()` but overrides
`getUsedLabelsInLine()` for custom label detection
- Different return formats: VC/EWAVR return `{asm: [...]}`, SPIRV
returns full `ParsedAsmResult` with `labelDefinitions`
## Test Plan
```bash
npm run test -- --run vc-asm-parser-tests.ts ewavr-asm-parser-tests.ts spirv-asm-parser-tests.ts asm-parser-subclass-integration-tests.ts
```
These tests establish the baseline behavior that must be preserved
during the upcoming AsmParser refactoring work to ensure subclass
compatibility.
🤖 Generated with [Claude Code](https://claude.ai/code)
---------
Co-authored-by: Claude <noreply@anthropic.com>
165 lines
6.6 KiB
TypeScript
165 lines
6.6 KiB
TypeScript
// Copyright (c) 2025, Compiler Explorer Authors
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
import {beforeEach, describe, expect, it, vi} from 'vitest';
|
|
|
|
import {SPIRVAsmParser} from '../lib/parsers/asm-parser-spirv.js';
|
|
|
|
describe('SPIRVAsmParser', () => {
|
|
let parser: SPIRVAsmParser;
|
|
|
|
beforeEach(() => {
|
|
parser = new SPIRVAsmParser();
|
|
});
|
|
|
|
describe('getUsedLabelsInLine override', () => {
|
|
it('should detect labels in OpFunctionCall', () => {
|
|
const line = 'OpFunctionCall %void %func_main';
|
|
const labels = parser.getUsedLabelsInLine(line);
|
|
|
|
expect(labels).toHaveLength(1);
|
|
expect(labels[0].name).toBe('%func_main');
|
|
});
|
|
|
|
it('should detect labels in OpBranch', () => {
|
|
const line = 'OpBranch %label_endif';
|
|
const labels = parser.getUsedLabelsInLine(line);
|
|
|
|
expect(labels).toHaveLength(1);
|
|
expect(labels[0].name).toBe('%label_endif');
|
|
});
|
|
|
|
it('should detect multiple labels in OpBranchConditional', () => {
|
|
const line = 'OpBranchConditional %cond %true_label %false_label';
|
|
const labels = parser.getUsedLabelsInLine(line);
|
|
|
|
expect(labels).toHaveLength(2);
|
|
expect(labels[0].name).toBe('%true_label');
|
|
expect(labels[1].name).toBe('%false_label');
|
|
});
|
|
|
|
it('should detect labels in OpSelectionMerge', () => {
|
|
const line = 'OpSelectionMerge %merge_label None';
|
|
const labels = parser.getUsedLabelsInLine(line);
|
|
|
|
expect(labels).toHaveLength(1);
|
|
expect(labels[0].name).toBe('%merge_label');
|
|
});
|
|
|
|
it('should detect labels in OpLoopMerge', () => {
|
|
const line = 'OpLoopMerge %merge_label %continue_label None';
|
|
const labels = parser.getUsedLabelsInLine(line);
|
|
|
|
expect(labels).toHaveLength(2);
|
|
expect(labels[0].name).toBe('%merge_label');
|
|
expect(labels[1].name).toBe('%continue_label');
|
|
});
|
|
|
|
it('should detect labels in OpSwitch', () => {
|
|
const line = 'OpSwitch %selector %default 1 %case1 2 %case2';
|
|
const labels = parser.getUsedLabelsInLine(line);
|
|
|
|
expect(labels).toHaveLength(3);
|
|
expect(labels[0].name).toBe('%default');
|
|
expect(labels[1].name).toBe('%case1');
|
|
expect(labels[2].name).toBe('%case2');
|
|
});
|
|
|
|
it('should be called during assembly processing', () => {
|
|
const spy = vi.spyOn(parser, 'getUsedLabelsInLine');
|
|
|
|
const spirvCode = `
|
|
OpBranch %exit_label
|
|
%exit_label = OpLabel
|
|
`;
|
|
|
|
parser.processAsm(spirvCode, {
|
|
directives: false,
|
|
labels: false,
|
|
commentOnly: false,
|
|
});
|
|
|
|
expect(spy).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('SPIR-V assembly processing', () => {
|
|
it('should detect SPIR-V percent labels with custom getUsedLabelsInLine', () => {
|
|
const spirvCode = [
|
|
'%main = OpFunction %void None %1',
|
|
'%entry = OpLabel',
|
|
'OpBranch %exit',
|
|
'%exit = OpLabel',
|
|
'OpReturn',
|
|
].join('\n');
|
|
|
|
const result = parser.processAsm(spirvCode, {
|
|
directives: false,
|
|
labels: false,
|
|
commentOnly: false,
|
|
});
|
|
|
|
// Should detect SPIR-V %label definitions
|
|
expect(result.labelDefinitions).toHaveProperty('%main');
|
|
expect(result.labelDefinitions).toHaveProperty('%entry');
|
|
expect(result.labelDefinitions).toHaveProperty('%exit');
|
|
|
|
// Should detect SPIR-V %label usage in OpBranch
|
|
const branchLine = result.asm.find(line => line.text?.includes('OpBranch'));
|
|
expect(branchLine?.labels?.some(label => label.name === '%exit')).toBe(true);
|
|
});
|
|
|
|
it('should handle SPIR-V control flow instructions with multiple label references', () => {
|
|
const spirvCode = [
|
|
'OpSelectionMerge %merge None',
|
|
'OpBranchConditional %condition %true_block %false_block',
|
|
'%true_block = OpLabel',
|
|
'%false_block = OpLabel',
|
|
'%merge = OpLabel',
|
|
].join('\n');
|
|
|
|
const result = parser.processAsm(spirvCode, {
|
|
directives: false,
|
|
labels: false,
|
|
commentOnly: false,
|
|
});
|
|
|
|
// Should identify all label definitions
|
|
expect(result.labelDefinitions).toHaveProperty('%true_block');
|
|
expect(result.labelDefinitions).toHaveProperty('%false_block');
|
|
expect(result.labelDefinitions).toHaveProperty('%merge');
|
|
|
|
// Should detect multiple labels in OpBranchConditional
|
|
const branchCondLine = result.asm.find(line => line.text?.includes('OpBranchConditional'));
|
|
const labelNames = branchCondLine?.labels?.map(label => label.name) || [];
|
|
expect(labelNames).toContain('%true_block');
|
|
expect(labelNames).toContain('%false_block');
|
|
|
|
// Should detect label in OpSelectionMerge
|
|
const mergeLine = result.asm.find(line => line.text?.includes('OpSelectionMerge'));
|
|
expect(mergeLine?.labels?.some(label => label.name === '%merge')).toBe(true);
|
|
});
|
|
});
|
|
});
|