Skip to content

Commit

Permalink
feat: add basic unions implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Mati365 committed Dec 29, 2023
1 parent 628e040 commit f03f75f
Show file tree
Hide file tree
Showing 15 changed files with 277 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import chalk from 'chalk';
import { CFunctionCallConvention } from '#constants';
import { IRVariable, isIRVariable } from 'frontend/ir/variables';
import { CBackendError, CBackendErrorCode } from 'backend/errors/CBackendError';
import { isStructLikeType } from 'frontend/analyze';
import { isStructLikeType, isUnionLikeType } from 'frontend/analyze';

import { getBaseTypeIfPtr } from 'frontend/analyze/types/utils';
import { getTypeOffsetByteSize } from 'frontend/ir/utils';
Expand Down Expand Up @@ -63,7 +63,7 @@ export class X86StdcallFnCaller implements X86ConventionalFnCaller {
const arg = callerInstruction.args[i];
const baseType = getBaseTypeIfPtr(arg.type);

if (isStructLikeType(baseType)) {
if (isStructLikeType(baseType) || isUnionLikeType(baseType)) {
if (!isIRVariable(arg)) {
throw new CBackendError(CBackendErrorCode.NON_CALLABLE_STRUCT_ARG);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export function compileStoreInstruction({
} else {
// handle normal variable assign
// *(a) = 5;
// todo: check if this .isStruct() is needed:
// todo: check if this .isStructOrUnion() is needed:
// char b = 'b';
// int k = b;
// struct Abc {
Expand All @@ -88,8 +88,8 @@ export function compileStoreInstruction({
if (isIRVariable(value)) {
// check if variable is struct or something bigger than that
if (
getBaseTypeIfPtr(value.type).isStruct() &&
getBaseTypeIfPtr(outputVar.type).isStruct() &&
getBaseTypeIfPtr(value.type).isStructOrUnion() &&
getBaseTypeIfPtr(outputVar.type).isStructOrUnion() &&
(!value.isTemporary() ||
isLabelOwnership(regs.ownership.getVarOwnership(value.name)))
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
isIRVariable,
} from 'frontend/ir/variables';

import { isStructLikeType } from 'frontend/analyze';
import { isStructLikeType, isUnionLikeType } from 'frontend/analyze';

import { getByteSizeArgPrefixName } from '@ts-c-compiler/x86-assembler';
import {
Expand Down Expand Up @@ -123,7 +123,9 @@ export class X86BasicRegAllocator {

if (
!castToPointerIfArray(arg.type).isScalar() &&
(!isStructLikeType(arg.type) || !arg.type.canBeStoredInReg())
(!isUnionLikeType(arg.type) ||
!isStructLikeType(arg.type) ||
!arg.type.canBeStoredInReg())
) {
throw new CBackendError(CBackendErrorCode.REG_ALLOCATOR_ERROR);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
typeofValueOrNode,
CPrimitiveType,
isPrimitiveLikeType,
isUnionLikeType,
} from '../../../types';

import {
Expand Down Expand Up @@ -249,7 +250,7 @@ export class CTypeInitializerBuilderVisitor extends CInnerTypeTreeVisitor {

// .x = 1
if (identifier) {
if (!isStructLikeType(baseType)) {
if (!isStructLikeType(baseType) && !isUnionLikeType(baseType)) {
throw new CTypeCheckError(
CTypeCheckErrorCode.INCORRECT_NAMED_STRUCTURE_INITIALIZER_USAGE,
designation.loc.start,
Expand All @@ -267,8 +268,11 @@ export class CTypeInitializerBuilderVisitor extends CInnerTypeTreeVisitor {
);
}

offset += field.index;
baseType = field.type;

if ('index' in field) {
offset += field.index;
}
}

// [10] = x
Expand Down Expand Up @@ -429,7 +433,7 @@ export class CTypeInitializerBuilderVisitor extends CInnerTypeTreeVisitor {
? entryValue.type.scalarValuesCount
: 1;

if (isStructLikeType(baseType)) {
if (isStructLikeType(baseType) || isUnionLikeType(baseType)) {
// increments offets, determine which field is initialized in struct and sets value
// used here: struct Vec2 vec = { 1, 2 };
tree.setAndExpand(this.currentOffset, entryValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import {
isArrayLikeType,
isPointerLikeType,
isStructLikeType,
isUnionLikeType,
} from '../../../types';

import { checkLeftTypeOverlapping } from '../../../checker';

export class ASTCPostfixExpressionTypeCreator extends ASTCTypeCreator<ASTCPostfixExpression> {
Expand Down Expand Up @@ -94,7 +96,7 @@ export class ASTCPostfixExpressionTypeCreator extends ASTCTypeCreator<ASTCPostfi
baseType = baseType.baseType;
}

if (isStructLikeType(baseType)) {
if (isStructLikeType(baseType) || isUnionLikeType(baseType)) {
if (!baseType.hasInnerTypeAttributes()) {
throw new CTypeCheckError(
CTypeCheckErrorCode.PROVIDED_TYPE_DOES_NOT_CONTAIN_PROPERTIES,
Expand All @@ -107,6 +109,7 @@ export class ASTCPostfixExpressionTypeCreator extends ASTCTypeCreator<ASTCPostfi

const { text: fieldName } = (node.dotExpression || node.ptrExpression)
.name;

const field = baseType.getField(fieldName);

if (!field) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { CTypedef } from './CTypedef';

type TypeFindAttrs = {
struct?: boolean;
union?: boolean;
primitive?: boolean;
enumerator?: boolean;
function?: boolean;
Expand Down Expand Up @@ -235,6 +236,7 @@ export class CScopeTree<C extends ASTCCompilerNode = ASTCCompilerNode>
function: fn,
primitive,
struct,
union,
enumerator,
} = attrs;

Expand All @@ -243,6 +245,7 @@ export class CScopeTree<C extends ASTCCompilerNode = ASTCCompilerNode>
if (
(primitive && !type.isPrimitive()) ||
(struct && !type.isStruct()) ||
(union && !type.isUnion()) ||
(enumerator && !type.isEnum()) ||
(fn && !type.isFunction())
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
isArrayLikeType,
isPointerLikeType,
isStructLikeType,
isUnionLikeType,
} from '../../types';

import { ASTCCompilerNode } from '../../../parser/ast/ASTCCompilerNode';
Expand Down Expand Up @@ -190,7 +191,7 @@ export class CVariableInitializerTree<
getIndexExpectedType(offset: number): CType {
const { baseType } = this;

if (isStructLikeType(baseType)) {
if (isStructLikeType(baseType) || isUnionLikeType(baseType)) {
return baseType.getFieldTypeByIndex(
offset % baseType.getFlattenFieldsCount(),
);
Expand All @@ -199,7 +200,7 @@ export class CVariableInitializerTree<
if (isArrayLikeType(baseType)) {
const baseArrayType = baseType.getSourceType();

if (isStructLikeType(baseArrayType)) {
if (isStructLikeType(baseArrayType) || isUnionLikeType(baseArrayType)) {
return baseArrayType.getFieldTypeByIndex(
offset % baseArrayType.getFlattenFieldsCount(),
);
Expand Down
6 changes: 5 additions & 1 deletion packages/compiler-pico-c/src/frontend/analyze/types/CType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ export abstract class CType<T extends CTypeDescriptor = CTypeDescriptor>
return false;
}

isStructOrUnion() {
return this.isUnion() || this.isStruct();
}

isFunction() {
return false;
}
Expand All @@ -141,7 +145,7 @@ export abstract class CType<T extends CTypeDescriptor = CTypeDescriptor>
}

hasInnerTypeAttributes() {
return this.isEnum() || this.isStruct();
return this.isEnum() || this.isStruct() || this.isUnion();
}

isConst() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
isPointerArithmeticType,
isPointerLikeType,
isStructLikeType,
isUnionLikeType,
} from 'frontend/analyze';

import {
Expand Down Expand Up @@ -345,7 +346,7 @@ export function emitExpressionIR({

if (
!srcGlobalVar.virtualArrayPtr &&
!getBaseTypeIfPtr(srcGlobalVar.type).isStruct()
!getBaseTypeIfPtr(srcGlobalVar.type).isStructOrUnion()
) {
const tmpDestVar = allocNextVariable(
getBaseTypeIfPtr(srcGlobalVar.type),
Expand Down Expand Up @@ -375,7 +376,8 @@ export function emitExpressionIR({

instructions.push(new IRLeaInstruction(srcVar, tmpVar));
} else if (
isStructLikeType(srcVar.type.baseType) &&
(isStructLikeType(srcVar.type.baseType) ||
isUnionLikeType(srcVar.type.baseType)) &&
!srcVar.type.baseType.canBeStoredInReg()
) {
// handle `a = vec` assign where `vec` is structure that
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as R from 'ramda';

import { isCompilerTreeNode } from 'frontend/parser';
import {
CPointerType,
CVariableInitializerTree,
isInitializerTreeValue,
} from 'frontend/analyze';
Expand All @@ -25,6 +26,7 @@ import {
} from './literal';

import { shouldEmitStringPtrInitializer } from './literal/shouldEmitStringPtrInitializer';
import { getBaseTypeIfPtr } from 'frontend/analyze/types/utils';

type LoadInitializerIREmitAttrs = IREmitterContextAttrs & {
initializerTree: CVariableInitializerTree;
Expand All @@ -41,6 +43,8 @@ export function emitVariableLoadInitializerIR({
context,
}: LoadInitializerIREmitAttrs): IREmitterStmtResult {
const result = createBlankStmtResult();
const isDestUnion = getBaseTypeIfPtr(destVar.type).isUnion();

let offset: number = 0;

initializerTree.fields.forEach((initializer, index) => {
Expand Down Expand Up @@ -104,7 +108,9 @@ export function emitVariableLoadInitializerIR({
result.instructions.push(
new IRStoreInstruction(
IRConstant.ofConstant(itemOffsetType, initializer),
destVar,
isDestUnion
? destVar.ofType(CPointerType.ofType(itemOffsetType))
: destVar,
offset,
),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
isArrayLikeType,
isPointerLikeType,
isStructLikeType,
isUnionLikeType,
} from 'frontend/analyze';

import { CUnaryCastOperator } from '#constants';
Expand Down Expand Up @@ -180,7 +181,8 @@ export function emitIdentifierGetterIR({
const parentType = getParentType();
if (
!isPointerLikeType(parentType) ||
!isStructLikeType(parentType.baseType)
(!isStructLikeType(parentType.baseType) &&
!isUnionLikeType(parentType.baseType))
) {
throw new IRError(IRErrorCode.ACCESS_STRUCT_ATTR_IN_NON_STRUCT);
}
Expand All @@ -194,12 +196,24 @@ export function emitIdentifierGetterIR({
),
);

const offsetConstant = IRConstant.ofConstant(
CPrimitiveType.int(config.arch),
parentType.baseType.getField(expr.name.text).offset,
);
if (isUnionLikeType(parentType.baseType)) {
lastIRVar = lastIRVar.ofType(
CPointerType.ofType(
parentType.baseType.getField(expr.name.text).type,
),
);

return false;
}

const offset = parentType.baseType.getField(expr.name.text).offset;

if (offset) {
const offsetConstant = IRConstant.ofConstant(
CPrimitiveType.int(config.arch),
offset,
);

if (offsetConstant.constant) {
instructions.push(
new IRMathInstruction(
TokenType.PLUS,
Expand Down Expand Up @@ -227,13 +241,14 @@ export function emitIdentifierGetterIR({
}

const parentType = getParentType();
if (!isStructLikeType(parentType)) {
if (!isStructLikeType(parentType) && !isUnionLikeType(parentType)) {
throw new IRError(IRErrorCode.ACCESS_STRUCT_ATTR_IN_NON_STRUCT);
}

if (
isPointerLikeType(lastIRVar.type) &&
isStructLikeType(lastIRVar.type.baseType) &&
(isStructLikeType(lastIRVar.type.baseType) ||
isUnionLikeType(lastIRVar.type.baseType)) &&
!lastIRVar.isTemporary()
) {
instructions.push(
Expand All @@ -244,12 +259,22 @@ export function emitIdentifierGetterIR({
);
}

const offsetConstant = IRConstant.ofConstant(
CPrimitiveType.int(config.arch),
parentType.getField(expr.name.text).offset,
);
if (isUnionLikeType(parentType)) {
lastIRVar = lastIRVar.ofType(
CPointerType.ofType(parentType.getField(expr.name.text).type),
);

return false;
}

const offset = parentType.getField(expr.name.text).offset;

if (offset) {
const offsetConstant = IRConstant.ofConstant(
CPrimitiveType.int(config.arch),
offset,
);

if (offsetConstant.constant) {
instructions.push(
new IRMathInstruction(
TokenType.PLUS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class IRLeaInstruction
extends IRInstruction
implements IsOutputInstruction
{
constructor(readonly inputVar: IRVariable, readonly outputVar: IRVariable) {
constructor(public inputVar: IRVariable, public outputVar: IRVariable) {
super(IROpcode.LEA);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,15 @@ export function dropRedundantAddressInstructions(
);

if (optimizedInstruction) {
newInstructions[i] = optimizedInstruction;
const optimizedArgs = optimizedInstruction.getArgs();

// force preserve output type due to issues with unions
newInstructions[i] = optimizedInstruction.ofArgs({
input: optimizedArgs.input,
output: optimizedArgs.output.ofType(
instruction.getArgs().output.type,
),
});
}
}

Expand Down
Loading

0 comments on commit f03f75f

Please sign in to comment.