public ExpressionResult Compile( ArrayAllocationExpression expression, PreferredRegister target, ICompilationContext context ) { var codeGen = context.Generator; var linkingInfo = context.Linking; var index = context.CompileExpression(expression.Count, new PreferredRegister(Register64.R8)); var indexType = index.ValueType; var valueTypeSize = (sbyte)expression.Type.SizeOf(); Constants.LongType.AssertCanAssignImplicitly(indexType); // Array allocation compiles to function call of HeapAlloc (kernel32) index.GenerateMoveTo(Register64.R8, Constants.LongType, codeGen, linkingInfo); // Parameter 3: byte count if (valueTypeSize > 1) { codeGen.Shl(Register64.R8, (sbyte)Math.Log(valueTypeSize, 2)); // 8 byte type -> multiply count by 8 by shifting left 3 } codeGen.Add(Register64.R8, (sbyte)8); // llama length value codeGen.MovFromDereferenced4(Register64.RCX, Constants.DummyOffsetInt); linkingInfo.FixDataOffset(codeGen.StreamPosition, Constants.HeapHandleIdentifier); // Parameter 1: DefaultHeapHandle codeGen.Xor(Register64.RDX, Register64.RDX); codeGen.Add(Register64.RDX, (sbyte)(0x8 + 0x4)); // Parameter 2: HEAP_ZERO_MEMORY + HEAP_GENERATE_EXCEPTIONS codeGen.CallDereferenced4(Constants.DummyOffsetInt); linkingInfo.FixIATEntryOffset(codeGen.StreamPosition, "kernel32.dll", "HeapAlloc"); return(new ExpressionResult(new Type(expression.Type, Type.WrappingType.ArrayOf), Register64.RAX)); }
public ExpressionResult Compile( IExpression expression, PreferredRegister target, ICompilationContext context ) { switch (expression) { case ArrayAccessExpression arrayAccessExpression: return(context.CompileExpression(arrayAccessExpression, target)); case ArrayAllocationExpression arrayAllocationExpression: return(context.CompileExpression(arrayAllocationExpression, target)); case AtomicExpression atomicExpression: return(context.CompileExpression(atomicExpression, target)); case BinaryOperatorExpression binaryOperatorExpression: return(context.CompileExpression(binaryOperatorExpression, target)); case FunctionCallExpression methodCallExpression: return(context.CompileExpression(methodCallExpression, target)); case TypeCastExpression typeCastExpression: return(context.CompileExpression(typeCastExpression, target)); case UnaryOperatorExpression unaryOperatorExpression: return(context.CompileExpression(unaryOperatorExpression, target)); default: throw new NotImplementedException($"Compiler for expression type \"{expression.GetType().Name}\" has not yet been implemented"); } }
/// <summary> /// Evaluates two expressions, and brings them to a common type. Tries to keep the second expression as reference, for /// mor optimal code generation. /// </summary> /// <param name="first"></param> /// <param name="second"></param> /// <param name="preferredFirst"></param> /// <param name="codeGen"></param> /// <param name="storageManager"></param> /// <param name="scope"></param> /// <param name="linkingInfo"></param> /// <param name="context"></param> /// <param name="shouldDereferenceForImplicitCast">The reference may be deferenced to cast it to the common type</param> /// <returns></returns> private static (Register register, ExpressionResult reference, Type type) PrepareBinaryExpression( IExpression first, IExpression second, PreferredRegister preferredFirst, ICompilationContext context, bool shouldDereferenceForImplicitCast = false ) { var firstResult = context.CompileExpression(first, preferredFirst); var isfirstIntegerType = firstResult.ValueType.IsIntegerRegisterType(); var firstTemp = context.Storage.Allocate(isfirstIntegerType); firstTemp.Store(firstResult, context.Generator, context.Linking); var secondResult = context.CompileExpression(second, preferredFirst); var type = firstResult.ValueType; if (firstResult.ValueType != secondResult.ValueType) { if (secondResult.ValueType.CanAssignImplicitly(firstResult.ValueType)) { type = secondResult.ValueType; } else if (shouldDereferenceForImplicitCast && firstResult.ValueType.CanAssignImplicitly(secondResult.ValueType)) { type = firstResult.ValueType; var secondDerefVolatile = secondResult.GetOccupiedOrVolatile(type); secondResult.GenerateMoveTo(secondDerefVolatile, type, context.Generator, context.Linking); secondResult = new ExpressionResult(type, secondDerefVolatile); } else { throw new TypeMismatchException("Any common type", $"{firstResult.ValueType} and {secondResult.ValueType}"); } } var preferredRegister = preferredFirst.MakeFor(type); var firstRegister = secondResult.IsOccopied(preferredRegister) ? secondResult.GetUnoccupiedVolatile(type) : preferredRegister; firstTemp.AsExpressionResult(firstResult.ValueType).GenerateMoveTo(firstRegister, type, context.Generator, context.Linking); context.Storage.Release(firstTemp); return(firstRegister, secondResult, type); }
public ExpressionResult Compile( ArrayAccessExpression expression, PreferredRegister target, ICompilationContext context ) { var arrayTemp = context.Storage.Allocate(true); var array = context.CompileExpression(expression.Array, arrayTemp.IsRegister ? arrayTemp.Register : Register64.RAX); var arrayType = array.ValueType; arrayTemp.Store(array, context.Generator, context.Linking); const Register64 structOffsetRegister = Register64.RCX; const Register64 arrayRegister = Register64.RAX; var arrayIndex = context.CompileExpression(expression.Index, new PreferredRegister(structOffsetRegister)); Constants.LongType.AssertCanAssignImplicitly(arrayIndex.ValueType); arrayIndex.GenerateMoveTo(structOffsetRegister, Constants.LongType, context.Generator, context.Linking); arrayTemp.AsExpressionResult(arrayType).GenerateMoveTo(arrayRegister, context.Generator, context.Linking); context.Storage.Release(arrayTemp); if (arrayType == Constants.CstrType) { return(new ExpressionResult(Constants.SbyteType, arrayRegister, structOffsetRegister, 1)); } var itemType = arrayType.Child; if (arrayType.ChildRelation == Type.WrappingType.PointerOf) { return(new ExpressionResult(itemType, arrayRegister, structOffsetRegister, (byte)itemType.SizeOf())); } if (arrayType.ChildRelation == Type.WrappingType.ArrayOf) { return(new ExpressionResult(itemType, arrayRegister, structOffsetRegister, (byte)itemType.SizeOf(), 8)); } throw new TypeMismatchException("Array or pointer", arrayType.ToString()); }
public void Compile( CodeBlock statement, ICompilationContext context ) { context.Symbols.PushLocalScope(); foreach (var subStatement in statement.Statements) { switch (subStatement) { case CodeBlock codeBlock: Compile(codeBlock, context); break; case Declaration declaration: context.CompileStatement(declaration); break; case For @for: context.CompileStatement(@for); break; case If @if: context.CompileStatement(@if); break; case While @while: context.CompileStatement(@while); break; case Return @return: context.CompileStatement(@return); break; case IExpression expression: context.CompileExpression( expression, new PreferredRegister(Register64.RAX, XmmRegister.XMM0) ); break; default: throw new NotImplementedException( $"{nameof(CodeBlockCompiler)}: I do not know how to compile: {subStatement.GetType().Name}" ); } } context.Symbols.PopLocalScope(); }
public void Compile( While statement, ICompilationContext context ) { var codeGen = context.Generator; var conditionStart = codeGen.StreamPosition; var whileConditionResult = context.CompileExpression(statement.Condition, new PreferredRegister(Register64.RAX)); var whileConditionRegister = whileConditionResult.GetOccupiedOrVolatile(Constants.BoolType); whileConditionResult.GenerateMoveTo(whileConditionRegister, Constants.BoolType, codeGen, context.Linking); codeGen.Test(whileConditionRegister, whileConditionRegister); var childContext = context.CreateChildContext(); childContext.CompileStatement(statement.Instruction.StatementAsBlock()); var bodySpan = childContext.Generator.GetBufferSpan(); var bodyLength = bodySpan.Length + JmpIntSize; // assume far jmp will be generated if (bodyLength <= sbyte.MaxValue) { codeGen.Je((sbyte)bodyLength); } else { codeGen.Je(bodyLength); } var farJmpGuessPos = codeGen.StreamPosition; childContext.CopyToContext(context); var offsetToStart = conditionStart - codeGen.StreamPosition; if (offsetToStart >= sbyte.MinValue) { codeGen.Jmp((sbyte)(offsetToStart - JmpSbyteSize)); // near jmp has been generated, but we assume a far jmp above // if this is a near jmp, the other has to be too // so it's safe to say, we just need to edit the byte. // The new value has to fit, because the jmp becomes even nearer. codeGen.GetBufferSpan().Slice((int)farJmpGuessPos - 1, 1)[0] -= (byte)(JmpIntSize - JmpSbyteSize); } else { codeGen.Jmp((int)(offsetToStart - JmpIntSize)); } }
public void Compile( If statement, ICompilationContext context ) { var preferredRegisterCondition = new PreferredRegister(Register64.RAX); var ifConditionResult = context.CompileExpression(statement.Condition, preferredRegisterCondition); ifConditionResult.GenerateMoveTo(preferredRegisterCondition.MakeFor(Constants.BoolType), context.Generator, context.Linking); context.Generator.Test(Register8.AL, Register8.AL); var ifBody = context.CreateChildContext(); var elseBody = context.CreateChildContext(); if (statement.ElseInstruction != null) { elseBody.CompileStatement(statement.ElseInstruction.StatementAsBlock()); } ifBody.CompileStatement(statement.Instruction.StatementAsBlock()); var elseBodySpan = elseBody.Generator.GetBufferSpan(); if (elseBodySpan.Length > 0) { if (elseBodySpan.Length <= sbyte.MaxValue) { ifBody.Generator.Jmp((sbyte)elseBodySpan.Length); } else { ifBody.Generator.Jmp(elseBodySpan.Length); } } var ifBodySpan = ifBody.Generator.GetBufferSpan(); if (ifBodySpan.Length <= sbyte.MaxValue) { context.Generator.Je((sbyte)ifBodySpan.Length); } else { context.Generator.Je(ifBodySpan.Length); } ifBody.CopyToContext(context); elseBody.CopyToContext(context); }
public void Compile(Declaration statement, ICompilationContext context) { context.Symbols.DefineLocal(statement.Identifier.RawText, statement.Type); if (statement.InitialValue == null) { context.Generator.Xor(Register64.RAX, Register64.RAX); context.Generator.MovToDereferenced(Register64.RSP, Register64.RAX, context.Symbols.GetLocalOffset(statement.Identifier.RawText)); } else { var initialValue = context.CompileExpression(statement.InitialValue, PreferredRegister.DefaultVolatile); var initialValueRegister = initialValue.ToRegister(statement.Type, context.Generator, context.Linking); context.Symbols.GetLocalReference(statement.Identifier.RawText) .GenerateAssign(initialValueRegister, context.Generator, context.Linking); } }
public void Compile( Return statement, ICompilationContext context ) { if (statement.ReturnValue != null) { var returnRegisters = new PreferredRegister(Register64.RAX, XmmRegister.XMM0); var returnResult = context.CompileExpression(statement.ReturnValue, returnRegisters); var myFunction = context.Symbols.GetFunctionDeclaration(context.Symbols.CurrentFunctionIdentifier); returnResult.GenerateMoveTo(returnRegisters.MakeFor(myFunction.ReturnType), myFunction.ReturnType, context.Generator, context.Linking); } context.Generator.Jmp(Constants.DummyOffsetInt); context.Linking.FixFunctionEpilogueOffset(context.Generator.StreamPosition, context.Symbols.CurrentFunctionIdentifier); }
public ExpressionResult Compile( TypeCastExpression expression, PreferredRegister target, ICompilationContext context ) { var source = context.CompileExpression(expression.CastExpression, target); var sourceType = source.ValueType; var targetType = expression.Type; if (sourceType == targetType) { return(source); } if (CanCastUnsafe(targetType, sourceType)) { source.ChangeTypeUnsafe(targetType); return(source); } var targetRegister = target.MakeFor(targetType); if (targetType.ChildRelation == Type.WrappingType.PointerOf && // Allow array to pointer casts if the underlying type is identical sourceType.ChildRelation == Type.WrappingType.ArrayOf && sourceType.Child == targetType.Child) { source.GenerateMoveTo(targetRegister, context.Generator, context.Linking); context.Generator.Add(targetRegister.AsR64(), (sbyte)8); return(new ExpressionResult(targetType, targetRegister)); } // we're done with pointer and array casting. Only value casting down below if (targetType.ChildRelation != Type.WrappingType.None || sourceType.ChildRelation != Type.WrappingType.None) { ThrowBadCast(sourceType, targetType); } var sourceTypeSize = sourceType.SizeOf(); var targetTypeSize = targetType.SizeOf(); var sourceIsInt = sourceType.IsIntegerRegisterType(); var targetIsInt = targetType.IsIntegerRegisterType(); if (sourceType == Constants.CstrType || targetType == Constants.CstrType) { ThrowBadCast(sourceType, targetType); } if (sourceIsInt && targetIsInt) { if (targetTypeSize > sourceTypeSize) { // int register widening - works implicitly - isCast flag given to do signed <-> unsigned conversion by force source.GenerateMoveTo(targetRegister, targetType, context.Generator, context.Linking, true); return(new ExpressionResult(targetType, targetRegister)); } // int register narrowing source.GenerateMoveTo(targetRegister, context.Generator, context.Linking); // // clean rest of register //if (targetTypeSize == 4) // codeGen.Mov(targetRegister.AsR32(), targetRegister.AsR32()); //else // codeGen.And(targetRegister.AsR32(), targetTypeSize == 2 ? ushort.MaxValue : byte.MaxValue); return(new ExpressionResult(targetType, targetRegister)); } if (!sourceIsInt && !targetIsInt) // float <-> double conversions { source.GenerateMoveTo(targetRegister, context.Generator, context.Linking); if (targetTypeSize < sourceTypeSize) { context.Generator.CvtSd2Ss(targetRegister.AsFloat(), targetRegister.AsFloat()); } else { context.Generator.CvtSs2Sd(targetRegister.AsFloat(), targetRegister.AsFloat()); } return(new ExpressionResult(targetType, targetRegister)); } if (!sourceIsInt) /* && targetIsInt) */ { return(CastFloatToInt(sourceType, targetType, targetRegister, source, context.Generator, context.Linking)); } /* if (!targetIsInt && sourceIsInt) */ return(CastIntToFloat(sourceType, targetType, targetRegister, source, context.Generator, context.Linking)); }