From 9a6babf482047042d71d3d390933e08b7fb7c925 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Tue, 19 Jan 2021 00:38:39 +0100 Subject: SPIR-V: Add generated specification --- src/codegen/spirv/spec.zig | 1645 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1645 insertions(+) create mode 100644 src/codegen/spirv/spec.zig (limited to 'src/codegen') diff --git a/src/codegen/spirv/spec.zig b/src/codegen/spirv/spec.zig new file mode 100644 index 0000000000..ceb62f1e5d --- /dev/null +++ b/src/codegen/spirv/spec.zig @@ -0,0 +1,1645 @@ +// Copyright (c) 2014-2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. +const Version = @import("builtin").Version; +pub const version = Version{ .major = 1, .minor = 5, .patch = 4 }; +pub const magic_number: u32 = 0x07230203; +pub const Opcode = extern enum(u16) { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpTerminateInvocation = 4416, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpTraceRayKHR = 4445, + OpExecuteCallableKHR = 4446, + OpConvertUToAccelerationStructureKHR = 4447, + OpIgnoreIntersectionKHR = 4448, + OpTerminateRayKHR = 4449, + OpTypeRayQueryKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpImageSampleFootprintNV = 5283, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpReportIntersectionNV = 5334, + OpReportIntersectionKHR = 5334, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTypeAccelerationStructureNV = 5341, + OpTypeAccelerationStructureKHR = 5341, + OpExecuteCallableNV = 5344, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpFunctionPointerINTEL = 5600, + OpFunctionPointerCallINTEL = 5601, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpLoopControlINTEL = 5887, + OpReadPipeBlockingINTEL = 5946, + OpWritePipeBlockingINTEL = 5947, + OpFPGARegINTEL = 5949, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpAtomicFAddEXT = 6035, + _, +}; +pub const ImageOperands = packed struct { + Bias: bool align(@alignOf(u32)) = false, + Lod: bool = false, + Grad: bool = false, + ConstOffset: bool = false, + Offset: bool = false, + ConstOffsets: bool = false, + Sample: bool = false, + MinLod: bool = false, + MakeTexelAvailable: bool = false, + MakeTexelVisible: bool = false, + NonPrivateTexel: bool = false, + VolatileTexel: bool = false, + SignExtend: bool = false, + ZeroExtend: bool = false, + _reserved_bit_14: bool = false, + _reserved_bit_15: bool = false, + _reserved_bit_16: bool = false, + _reserved_bit_17: bool = false, + _reserved_bit_18: bool = false, + _reserved_bit_19: bool = false, + _reserved_bit_20: bool = false, + _reserved_bit_21: bool = false, + _reserved_bit_22: bool = false, + _reserved_bit_23: bool = false, + _reserved_bit_24: bool = false, + _reserved_bit_25: bool = false, + _reserved_bit_26: bool = false, + _reserved_bit_27: bool = false, + _reserved_bit_28: bool = false, + _reserved_bit_29: bool = false, + _reserved_bit_30: bool = false, + _reserved_bit_31: bool = false, +}; +pub const FPFastMathMode = packed struct { + NotNaN: bool align(@alignOf(u32)) = false, + NotInf: bool = false, + NSZ: bool = false, + AllowRecip: bool = false, + Fast: bool = false, + _reserved_bit_5: bool = false, + _reserved_bit_6: bool = false, + _reserved_bit_7: bool = false, + _reserved_bit_8: bool = false, + _reserved_bit_9: bool = false, + _reserved_bit_10: bool = false, + _reserved_bit_11: bool = false, + _reserved_bit_12: bool = false, + _reserved_bit_13: bool = false, + _reserved_bit_14: bool = false, + _reserved_bit_15: bool = false, + _reserved_bit_16: bool = false, + _reserved_bit_17: bool = false, + _reserved_bit_18: bool = false, + _reserved_bit_19: bool = false, + _reserved_bit_20: bool = false, + _reserved_bit_21: bool = false, + _reserved_bit_22: bool = false, + _reserved_bit_23: bool = false, + _reserved_bit_24: bool = false, + _reserved_bit_25: bool = false, + _reserved_bit_26: bool = false, + _reserved_bit_27: bool = false, + _reserved_bit_28: bool = false, + _reserved_bit_29: bool = false, + _reserved_bit_30: bool = false, + _reserved_bit_31: bool = false, +}; +pub const SelectionControl = packed struct { + Flatten: bool align(@alignOf(u32)) = false, + DontFlatten: bool = false, + _reserved_bit_2: bool = false, + _reserved_bit_3: bool = false, + _reserved_bit_4: bool = false, + _reserved_bit_5: bool = false, + _reserved_bit_6: bool = false, + _reserved_bit_7: bool = false, + _reserved_bit_8: bool = false, + _reserved_bit_9: bool = false, + _reserved_bit_10: bool = false, + _reserved_bit_11: bool = false, + _reserved_bit_12: bool = false, + _reserved_bit_13: bool = false, + _reserved_bit_14: bool = false, + _reserved_bit_15: bool = false, + _reserved_bit_16: bool = false, + _reserved_bit_17: bool = false, + _reserved_bit_18: bool = false, + _reserved_bit_19: bool = false, + _reserved_bit_20: bool = false, + _reserved_bit_21: bool = false, + _reserved_bit_22: bool = false, + _reserved_bit_23: bool = false, + _reserved_bit_24: bool = false, + _reserved_bit_25: bool = false, + _reserved_bit_26: bool = false, + _reserved_bit_27: bool = false, + _reserved_bit_28: bool = false, + _reserved_bit_29: bool = false, + _reserved_bit_30: bool = false, + _reserved_bit_31: bool = false, +}; +pub const LoopControl = packed struct { + Unroll: bool align(@alignOf(u32)) = false, + DontUnroll: bool = false, + DependencyInfinite: bool = false, + DependencyLength: bool = false, + MinIterations: bool = false, + MaxIterations: bool = false, + IterationMultiple: bool = false, + PeelCount: bool = false, + PartialCount: bool = false, + _reserved_bit_9: bool = false, + _reserved_bit_10: bool = false, + _reserved_bit_11: bool = false, + _reserved_bit_12: bool = false, + _reserved_bit_13: bool = false, + _reserved_bit_14: bool = false, + _reserved_bit_15: bool = false, + InitiationIntervalINTEL: bool = false, + MaxConcurrencyINTEL: bool = false, + DependencyArrayINTEL: bool = false, + PipelineEnableINTEL: bool = false, + LoopCoalesceINTEL: bool = false, + MaxInterleavingINTEL: bool = false, + SpeculatedIterationsINTEL: bool = false, + _reserved_bit_23: bool = false, + _reserved_bit_24: bool = false, + _reserved_bit_25: bool = false, + _reserved_bit_26: bool = false, + _reserved_bit_27: bool = false, + _reserved_bit_28: bool = false, + _reserved_bit_29: bool = false, + _reserved_bit_30: bool = false, + _reserved_bit_31: bool = false, +}; +pub const FunctionControl = packed struct { + Inline: bool align(@alignOf(u32)) = false, + DontInline: bool = false, + Pure: bool = false, + Const: bool = false, + _reserved_bit_4: bool = false, + _reserved_bit_5: bool = false, + _reserved_bit_6: bool = false, + _reserved_bit_7: bool = false, + _reserved_bit_8: bool = false, + _reserved_bit_9: bool = false, + _reserved_bit_10: bool = false, + _reserved_bit_11: bool = false, + _reserved_bit_12: bool = false, + _reserved_bit_13: bool = false, + _reserved_bit_14: bool = false, + _reserved_bit_15: bool = false, + _reserved_bit_16: bool = false, + _reserved_bit_17: bool = false, + _reserved_bit_18: bool = false, + _reserved_bit_19: bool = false, + _reserved_bit_20: bool = false, + _reserved_bit_21: bool = false, + _reserved_bit_22: bool = false, + _reserved_bit_23: bool = false, + _reserved_bit_24: bool = false, + _reserved_bit_25: bool = false, + _reserved_bit_26: bool = false, + _reserved_bit_27: bool = false, + _reserved_bit_28: bool = false, + _reserved_bit_29: bool = false, + _reserved_bit_30: bool = false, + _reserved_bit_31: bool = false, +}; +pub const MemorySemantics = packed struct { + _reserved_bit_0: bool align(@alignOf(u32)) = false, + Acquire: bool = false, + Release: bool = false, + AcquireRelease: bool = false, + SequentiallyConsistent: bool = false, + _reserved_bit_5: bool = false, + UniformMemory: bool = false, + SubgroupMemory: bool = false, + WorkgroupMemory: bool = false, + CrossWorkgroupMemory: bool = false, + AtomicCounterMemory: bool = false, + ImageMemory: bool = false, + OutputMemory: bool = false, + MakeAvailable: bool = false, + MakeVisible: bool = false, + Volatile: bool = false, + _reserved_bit_16: bool = false, + _reserved_bit_17: bool = false, + _reserved_bit_18: bool = false, + _reserved_bit_19: bool = false, + _reserved_bit_20: bool = false, + _reserved_bit_21: bool = false, + _reserved_bit_22: bool = false, + _reserved_bit_23: bool = false, + _reserved_bit_24: bool = false, + _reserved_bit_25: bool = false, + _reserved_bit_26: bool = false, + _reserved_bit_27: bool = false, + _reserved_bit_28: bool = false, + _reserved_bit_29: bool = false, + _reserved_bit_30: bool = false, + _reserved_bit_31: bool = false, +}; +pub const MemoryAccess = packed struct { + Volatile: bool align(@alignOf(u32)) = false, + Aligned: bool = false, + Nontemporal: bool = false, + MakePointerAvailable: bool = false, + MakePointerVisible: bool = false, + NonPrivatePointer: bool = false, + _reserved_bit_6: bool = false, + _reserved_bit_7: bool = false, + _reserved_bit_8: bool = false, + _reserved_bit_9: bool = false, + _reserved_bit_10: bool = false, + _reserved_bit_11: bool = false, + _reserved_bit_12: bool = false, + _reserved_bit_13: bool = false, + _reserved_bit_14: bool = false, + _reserved_bit_15: bool = false, + _reserved_bit_16: bool = false, + _reserved_bit_17: bool = false, + _reserved_bit_18: bool = false, + _reserved_bit_19: bool = false, + _reserved_bit_20: bool = false, + _reserved_bit_21: bool = false, + _reserved_bit_22: bool = false, + _reserved_bit_23: bool = false, + _reserved_bit_24: bool = false, + _reserved_bit_25: bool = false, + _reserved_bit_26: bool = false, + _reserved_bit_27: bool = false, + _reserved_bit_28: bool = false, + _reserved_bit_29: bool = false, + _reserved_bit_30: bool = false, + _reserved_bit_31: bool = false, +}; +pub const KernelProfilingInfo = packed struct { + CmdExecTime: bool align(@alignOf(u32)) = false, + _reserved_bit_1: bool = false, + _reserved_bit_2: bool = false, + _reserved_bit_3: bool = false, + _reserved_bit_4: bool = false, + _reserved_bit_5: bool = false, + _reserved_bit_6: bool = false, + _reserved_bit_7: bool = false, + _reserved_bit_8: bool = false, + _reserved_bit_9: bool = false, + _reserved_bit_10: bool = false, + _reserved_bit_11: bool = false, + _reserved_bit_12: bool = false, + _reserved_bit_13: bool = false, + _reserved_bit_14: bool = false, + _reserved_bit_15: bool = false, + _reserved_bit_16: bool = false, + _reserved_bit_17: bool = false, + _reserved_bit_18: bool = false, + _reserved_bit_19: bool = false, + _reserved_bit_20: bool = false, + _reserved_bit_21: bool = false, + _reserved_bit_22: bool = false, + _reserved_bit_23: bool = false, + _reserved_bit_24: bool = false, + _reserved_bit_25: bool = false, + _reserved_bit_26: bool = false, + _reserved_bit_27: bool = false, + _reserved_bit_28: bool = false, + _reserved_bit_29: bool = false, + _reserved_bit_30: bool = false, + _reserved_bit_31: bool = false, +}; +pub const RayFlags = packed struct { + OpaqueKHR: bool align(@alignOf(u32)) = false, + NoOpaqueKHR: bool = false, + TerminateOnFirstHitKHR: bool = false, + SkipClosestHitShaderKHR: bool = false, + CullBackFacingTrianglesKHR: bool = false, + CullFrontFacingTrianglesKHR: bool = false, + CullOpaqueKHR: bool = false, + CullNoOpaqueKHR: bool = false, + SkipTrianglesKHR: bool = false, + SkipAABBsKHR: bool = false, + _reserved_bit_10: bool = false, + _reserved_bit_11: bool = false, + _reserved_bit_12: bool = false, + _reserved_bit_13: bool = false, + _reserved_bit_14: bool = false, + _reserved_bit_15: bool = false, + _reserved_bit_16: bool = false, + _reserved_bit_17: bool = false, + _reserved_bit_18: bool = false, + _reserved_bit_19: bool = false, + _reserved_bit_20: bool = false, + _reserved_bit_21: bool = false, + _reserved_bit_22: bool = false, + _reserved_bit_23: bool = false, + _reserved_bit_24: bool = false, + _reserved_bit_25: bool = false, + _reserved_bit_26: bool = false, + _reserved_bit_27: bool = false, + _reserved_bit_28: bool = false, + _reserved_bit_29: bool = false, + _reserved_bit_30: bool = false, + _reserved_bit_31: bool = false, +}; +pub const FragmentShadingRate = packed struct { + Vertical2Pixels: bool align(@alignOf(u32)) = false, + Vertical4Pixels: bool = false, + Horizontal2Pixels: bool = false, + Horizontal4Pixels: bool = false, + _reserved_bit_4: bool = false, + _reserved_bit_5: bool = false, + _reserved_bit_6: bool = false, + _reserved_bit_7: bool = false, + _reserved_bit_8: bool = false, + _reserved_bit_9: bool = false, + _reserved_bit_10: bool = false, + _reserved_bit_11: bool = false, + _reserved_bit_12: bool = false, + _reserved_bit_13: bool = false, + _reserved_bit_14: bool = false, + _reserved_bit_15: bool = false, + _reserved_bit_16: bool = false, + _reserved_bit_17: bool = false, + _reserved_bit_18: bool = false, + _reserved_bit_19: bool = false, + _reserved_bit_20: bool = false, + _reserved_bit_21: bool = false, + _reserved_bit_22: bool = false, + _reserved_bit_23: bool = false, + _reserved_bit_24: bool = false, + _reserved_bit_25: bool = false, + _reserved_bit_26: bool = false, + _reserved_bit_27: bool = false, + _reserved_bit_28: bool = false, + _reserved_bit_29: bool = false, + _reserved_bit_30: bool = false, + _reserved_bit_31: bool = false, +}; +pub const SourceLanguage = extern enum(u32) { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + _, +}; +pub const ExecutionModel = extern enum(u32) { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + TaskNV = 5267, + MeshNV = 5268, + RayGenerationNV = 5313, + RayGenerationKHR = 5313, + IntersectionNV = 5314, + IntersectionKHR = 5314, + AnyHitNV = 5315, + AnyHitKHR = 5315, + ClosestHitNV = 5316, + ClosestHitKHR = 5316, + MissNV = 5317, + MissKHR = 5317, + CallableNV = 5318, + CallableKHR = 5318, + _, +}; +pub const AddressingModel = extern enum(u32) { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + PhysicalStorageBuffer64 = 5348, + PhysicalStorageBuffer64EXT = 5348, + _, +}; +pub const MemoryModel = extern enum(u32) { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Vulkan = 3, + VulkanKHR = 3, + _, +}; +pub const ExecutionMode = extern enum(u32) { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + PostDepthCoverage = 4446, + DenormPreserve = 4459, + DenormFlushToZero = 4460, + SignedZeroInfNanPreserve = 4461, + RoundingModeRTE = 4462, + RoundingModeRTZ = 4463, + StencilRefReplacingEXT = 5027, + OutputLinesNV = 5269, + OutputPrimitivesNV = 5270, + DerivativeGroupQuadsNV = 5289, + DerivativeGroupLinearNV = 5290, + OutputTrianglesNV = 5298, + PixelInterlockOrderedEXT = 5366, + PixelInterlockUnorderedEXT = 5367, + SampleInterlockOrderedEXT = 5368, + SampleInterlockUnorderedEXT = 5369, + ShadingRateInterlockOrderedEXT = 5370, + ShadingRateInterlockUnorderedEXT = 5371, + MaxWorkgroupSizeINTEL = 5893, + MaxWorkDimINTEL = 5894, + NoGlobalOffsetINTEL = 5895, + NumSIMDWorkitemsINTEL = 5896, + _, +}; +pub const StorageClass = extern enum(u32) { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + CallableDataNV = 5328, + CallableDataKHR = 5328, + IncomingCallableDataNV = 5329, + IncomingCallableDataKHR = 5329, + RayPayloadNV = 5338, + RayPayloadKHR = 5338, + HitAttributeNV = 5339, + HitAttributeKHR = 5339, + IncomingRayPayloadNV = 5342, + IncomingRayPayloadKHR = 5342, + ShaderRecordBufferNV = 5343, + ShaderRecordBufferKHR = 5343, + PhysicalStorageBuffer = 5349, + PhysicalStorageBufferEXT = 5349, + CodeSectionINTEL = 5605, + _, +}; +pub const Dim = extern enum(u32) { + @"1D" = 0, + @"2D" = 1, + @"3D" = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + _, +}; +pub const SamplerAddressingMode = extern enum(u32) { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + _, +}; +pub const SamplerFilterMode = extern enum(u32) { + Nearest = 0, + Linear = 1, + _, +}; +pub const ImageFormat = extern enum(u32) { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + R64ui = 40, + R64i = 41, + _, +}; +pub const ImageChannelOrder = extern enum(u32) { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + _, +}; +pub const ImageChannelDataType = extern enum(u32) { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + _, +}; +pub const FPRoundingMode = extern enum(u32) { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + _, +}; +pub const LinkageType = extern enum(u32) { + Export = 0, + Import = 1, + _, +}; +pub const AccessQualifier = extern enum(u32) { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + _, +}; +pub const FunctionParameterAttribute = extern enum(u32) { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + _, +}; +pub const Decoration = extern enum(u32) { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + UniformId = 27, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + NoSignedWrap = 4469, + NoUnsignedWrap = 4470, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + PerPrimitiveNV = 5271, + PerViewNV = 5272, + PerTaskNV = 5273, + PerVertexNV = 5285, + NonUniform = 5300, + NonUniformEXT = 5300, + RestrictPointer = 5355, + RestrictPointerEXT = 5355, + AliasedPointer = 5356, + AliasedPointerEXT = 5356, + ReferencedIndirectlyINTEL = 5602, + CounterBuffer = 5634, + HlslCounterBufferGOOGLE = 5634, + UserSemantic = 5635, + HlslSemanticGOOGLE = 5635, + UserTypeGOOGLE = 5636, + RegisterINTEL = 5825, + MemoryINTEL = 5826, + NumbanksINTEL = 5827, + BankwidthINTEL = 5828, + MaxPrivateCopiesINTEL = 5829, + SinglepumpINTEL = 5830, + DoublepumpINTEL = 5831, + MaxReplicatesINTEL = 5832, + SimpleDualPortINTEL = 5833, + MergeINTEL = 5834, + BankBitsINTEL = 5835, + ForcePow2DepthINTEL = 5836, + _, +}; +pub const BuiltIn = extern enum(u32) { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMask = 4416, + SubgroupGeMask = 4417, + SubgroupGtMask = 4418, + SubgroupLeMask = 4419, + SubgroupLtMask = 4420, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + PrimitiveShadingRateKHR = 4432, + DeviceIndex = 4438, + ViewIndex = 4440, + ShadingRateKHR = 4444, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + FullyCoveredEXT = 5264, + TaskCountNV = 5274, + PrimitiveCountNV = 5275, + PrimitiveIndicesNV = 5276, + ClipDistancePerViewNV = 5277, + CullDistancePerViewNV = 5278, + LayerPerViewNV = 5279, + MeshViewCountNV = 5280, + MeshViewIndicesNV = 5281, + BaryCoordNV = 5286, + BaryCoordNoPerspNV = 5287, + FragSizeEXT = 5292, + FragmentSizeNV = 5292, + FragInvocationCountEXT = 5293, + InvocationsPerPixelNV = 5293, + LaunchIdNV = 5319, + LaunchIdKHR = 5319, + LaunchSizeNV = 5320, + LaunchSizeKHR = 5320, + WorldRayOriginNV = 5321, + WorldRayOriginKHR = 5321, + WorldRayDirectionNV = 5322, + WorldRayDirectionKHR = 5322, + ObjectRayOriginNV = 5323, + ObjectRayOriginKHR = 5323, + ObjectRayDirectionNV = 5324, + ObjectRayDirectionKHR = 5324, + RayTminNV = 5325, + RayTminKHR = 5325, + RayTmaxNV = 5326, + RayTmaxKHR = 5326, + InstanceCustomIndexNV = 5327, + InstanceCustomIndexKHR = 5327, + ObjectToWorldNV = 5330, + ObjectToWorldKHR = 5330, + WorldToObjectNV = 5331, + WorldToObjectKHR = 5331, + HitTNV = 5332, + HitKindNV = 5333, + HitKindKHR = 5333, + IncomingRayFlagsNV = 5351, + IncomingRayFlagsKHR = 5351, + RayGeometryIndexKHR = 5352, + WarpsPerSMNV = 5374, + SMCountNV = 5375, + WarpIDNV = 5376, + SMIDNV = 5377, + _, +}; +pub const Scope = extern enum(u32) { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + QueueFamily = 5, + QueueFamilyKHR = 5, + ShaderCallKHR = 6, + _, +}; +pub const GroupOperation = extern enum(u32) { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + ClusteredReduce = 3, + PartitionedReduceNV = 6, + PartitionedInclusiveScanNV = 7, + PartitionedExclusiveScanNV = 8, + _, +}; +pub const KernelEnqueueFlags = extern enum(u32) { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + _, +}; +pub const Capability = extern enum(u32) { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + GroupNonUniform = 61, + GroupNonUniformVote = 62, + GroupNonUniformArithmetic = 63, + GroupNonUniformBallot = 64, + GroupNonUniformShuffle = 65, + GroupNonUniformShuffleRelative = 66, + GroupNonUniformClustered = 67, + GroupNonUniformQuad = 68, + ShaderLayer = 69, + ShaderViewportIndex = 70, + FragmentShadingRateKHR = 4422, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + UniformAndStorageBuffer16BitAccess = 4434, + StorageUniform16 = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + StorageBuffer8BitAccess = 4448, + UniformAndStorageBuffer8BitAccess = 4449, + StoragePushConstant8 = 4450, + DenormPreserve = 4464, + DenormFlushToZero = 4465, + SignedZeroInfNanPreserve = 4466, + RoundingModeRTE = 4467, + RoundingModeRTZ = 4468, + RayQueryProvisionalKHR = 4471, + RayQueryKHR = 4472, + RayTraversalPrimitiveCullingKHR = 4478, + RayTracingKHR = 4479, + Float16ImageAMD = 5008, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + Int64ImageEXT = 5016, + ShaderClockKHR = 5055, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + FragmentFullyCoveredEXT = 5265, + MeshShadingNV = 5266, + ImageFootprintNV = 5282, + FragmentBarycentricNV = 5284, + ComputeDerivativeGroupQuadsNV = 5288, + FragmentDensityEXT = 5291, + ShadingRateNV = 5291, + GroupNonUniformPartitionedNV = 5297, + ShaderNonUniform = 5301, + ShaderNonUniformEXT = 5301, + RuntimeDescriptorArray = 5302, + RuntimeDescriptorArrayEXT = 5302, + InputAttachmentArrayDynamicIndexing = 5303, + InputAttachmentArrayDynamicIndexingEXT = 5303, + UniformTexelBufferArrayDynamicIndexing = 5304, + UniformTexelBufferArrayDynamicIndexingEXT = 5304, + StorageTexelBufferArrayDynamicIndexing = 5305, + StorageTexelBufferArrayDynamicIndexingEXT = 5305, + UniformBufferArrayNonUniformIndexing = 5306, + UniformBufferArrayNonUniformIndexingEXT = 5306, + SampledImageArrayNonUniformIndexing = 5307, + SampledImageArrayNonUniformIndexingEXT = 5307, + StorageBufferArrayNonUniformIndexing = 5308, + StorageBufferArrayNonUniformIndexingEXT = 5308, + StorageImageArrayNonUniformIndexing = 5309, + StorageImageArrayNonUniformIndexingEXT = 5309, + InputAttachmentArrayNonUniformIndexing = 5310, + InputAttachmentArrayNonUniformIndexingEXT = 5310, + UniformTexelBufferArrayNonUniformIndexing = 5311, + UniformTexelBufferArrayNonUniformIndexingEXT = 5311, + StorageTexelBufferArrayNonUniformIndexing = 5312, + StorageTexelBufferArrayNonUniformIndexingEXT = 5312, + RayTracingNV = 5340, + VulkanMemoryModel = 5345, + VulkanMemoryModelKHR = 5345, + VulkanMemoryModelDeviceScope = 5346, + VulkanMemoryModelDeviceScopeKHR = 5346, + PhysicalStorageBufferAddresses = 5347, + PhysicalStorageBufferAddressesEXT = 5347, + ComputeDerivativeGroupLinearNV = 5350, + RayTracingProvisionalKHR = 5353, + CooperativeMatrixNV = 5357, + FragmentShaderSampleInterlockEXT = 5363, + FragmentShaderShadingRateInterlockEXT = 5372, + ShaderSMBuiltinsNV = 5373, + FragmentShaderPixelInterlockEXT = 5378, + DemoteToHelperInvocationEXT = 5379, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + SubgroupImageMediaBlockIOINTEL = 5579, + IntegerFunctions2INTEL = 5584, + FunctionPointersINTEL = 5603, + IndirectReferencesINTEL = 5604, + SubgroupAvcMotionEstimationINTEL = 5696, + SubgroupAvcMotionEstimationIntraINTEL = 5697, + SubgroupAvcMotionEstimationChromaINTEL = 5698, + FPGAMemoryAttributesINTEL = 5824, + UnstructuredLoopControlsINTEL = 5886, + FPGALoopControlsINTEL = 5888, + KernelAttributesINTEL = 5892, + FPGAKernelAttributesINTEL = 5897, + BlockingPipesINTEL = 5945, + FPGARegINTEL = 5948, + AtomicFloat32AddEXT = 6033, + AtomicFloat64AddEXT = 6034, + _, +}; +pub const RayQueryIntersection = extern enum(u32) { + RayQueryCandidateIntersectionKHR = 0, + RayQueryCommittedIntersectionKHR = 1, + _, +}; +pub const RayQueryCommittedIntersectionType = extern enum(u32) { + RayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionGeneratedKHR = 2, + _, +}; +pub const RayQueryCandidateIntersectionType = extern enum(u32) { + RayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionAABBKHR = 1, + _, +}; -- cgit v1.2.3 From b2b87b590011d8df52874e3f9bd1f88d1b0189d1 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Tue, 19 Jan 2021 00:34:44 +0100 Subject: SPIR-V: Linking and codegen setup --- src/Module.zig | 5 +- src/codegen/spirv.zig | 22 +++++++++ src/link.zig | 29 +++++++---- src/link/SpirV.zig | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.zig | 3 ++ 5 files changed, 180 insertions(+), 10 deletions(-) create mode 100644 src/codegen/spirv.zig create mode 100644 src/link/SpirV.zig (limited to 'src/codegen') diff --git a/src/Module.zig b/src/Module.zig index fa9722814e..464124c7b9 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1622,7 +1622,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void // in `Decl` to notice that the line number did not change. self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); }, - .c, .wasm => {}, + .c, .wasm, .spirv => {}, } } } else { @@ -1855,6 +1855,7 @@ fn allocateNewDecl( .macho => .{ .macho = link.File.MachO.TextBlock.empty }, .c => .{ .c = link.File.C.DeclBlock.empty }, .wasm => .{ .wasm = {} }, + .spirv => .{ .spirv = {} }, }, .fn_link = switch (mod.comp.bin_file.tag) { .coff => .{ .coff = {} }, @@ -1862,6 +1863,7 @@ fn allocateNewDecl( .macho => .{ .macho = link.File.MachO.SrcFn.empty }, .c => .{ .c = link.File.C.FnBlock.empty }, .wasm => .{ .wasm = null }, + .spirv => .{ .spirv = .{} }, }, .generation = 0, .is_pub = false, @@ -1959,6 +1961,7 @@ pub fn analyzeExport( .macho => .{ .macho = link.File.MachO.Export{} }, .c => .{ .c = {} }, .wasm => .{ .wasm = {} }, + .spirv => .{ .spirv = {} }, }, .owner_decl = owner_decl, .exported_decl = exported_decl, diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig new file mode 100644 index 0000000000..f0ebd49e1d --- /dev/null +++ b/src/codegen/spirv.zig @@ -0,0 +1,22 @@ +const std = @import("std"); +const spec = @import("spirv/spec.zig"); +const Module = @import("../Module.zig"); +const Decl = Module.Decl; + +pub const SPIRVModule = struct { + // TODO: Also use a free list. + next_id: u32 = 0, + + pub fn allocId(self: *SPIRVModule) u32 { + defer self.next_id += 1; + return self.next_id; + } + + pub fn idBound(self: *SPIRVModule) u32 { + return self.next_id; + } + + pub fn genDecl(self: SPIRVModule, id: u32, code: *std.ArrayList(u32), decl: *Decl) !void { + + } +}; diff --git a/src/link.zig b/src/link.zig index fdbb7efd4b..a19a261f55 100644 --- a/src/link.zig +++ b/src/link.zig @@ -142,7 +142,7 @@ pub const File = struct { macho: MachO.SrcFn, c: C.FnBlock, wasm: ?Wasm.FnData, - spirv: void, + spirv: SpirV.FnData, }; pub const Export = union { @@ -180,7 +180,7 @@ pub const File = struct { .macho => &(try MachO.createEmpty(allocator, options)).base, .wasm => &(try Wasm.createEmpty(allocator, options)).base, .c => unreachable, // Reported error earlier. - .spirv => return error.SpirVObjectFormatUnimplemented, + .spirv => &(try SpirV.createEmpty(allocator, options)).base, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, }; @@ -196,7 +196,7 @@ pub const File = struct { .macho => &(try MachO.createEmpty(allocator, options)).base, .wasm => &(try Wasm.createEmpty(allocator, options)).base, .c => unreachable, // Reported error earlier. - .spirv => return error.SpirVObjectFormatUnimplemented, + .spirv => &(try SpirV.createEmpty(allocator, options)).base, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, }; @@ -212,7 +212,7 @@ pub const File = struct { .macho => &(try MachO.openPath(allocator, sub_path, options)).base, .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base, .c => &(try C.openPath(allocator, sub_path, options)).base, - .spirv => return error.SpirVObjectFormatUnimplemented, + .spirv => &(try SpirV.openPath(allocator, sub_path, options)).base, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, }; @@ -242,7 +242,7 @@ pub const File = struct { .mode = determineMode(base.options), }); }, - .c, .wasm => {}, + .c, .wasm, .spirv => {}, } } @@ -287,7 +287,7 @@ pub const File = struct { f.close(); base.file = null; }, - .c, .wasm => {}, + .c, .wasm, .spirv => {}, } } @@ -300,6 +300,7 @@ pub const File = struct { .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl), .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl), .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl), + .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl), } } @@ -309,7 +310,7 @@ pub const File = struct { .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl), .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl), .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl), - .wasm => {}, + .wasm, .spirv => {}, } } @@ -321,7 +322,7 @@ pub const File = struct { .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl), .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl), .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl), - .wasm => {}, + .wasm, .spirv => {}, } } @@ -368,6 +369,11 @@ pub const File = struct { parent.deinit(); base.allocator.destroy(parent); }, + .spirv => { + const parent = @fieldParentPtr(SpirV, "base", base); + parent.deinit(); + base.allocator.destroy(parent); + }, } } @@ -401,6 +407,7 @@ pub const File = struct { .macho => return @fieldParentPtr(MachO, "base", base).flush(comp), .c => return @fieldParentPtr(C, "base", base).flush(comp), .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp), + .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp), } } @@ -413,6 +420,7 @@ pub const File = struct { .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp), .c => return @fieldParentPtr(C, "base", base).flushModule(comp), .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp), + .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp), } } @@ -424,6 +432,7 @@ pub const File = struct { .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl), .c => @fieldParentPtr(C, "base", base).freeDecl(decl), .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl), + .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl), } } @@ -433,7 +442,7 @@ pub const File = struct { .elf => return @fieldParentPtr(Elf, "base", base).error_flags, .macho => return @fieldParentPtr(MachO, "base", base).error_flags, .c => return .{ .no_entry_point_found = false }, - .wasm => return ErrorFlags{}, + .wasm, .spirv => return ErrorFlags{}, } } @@ -451,6 +460,7 @@ pub const File = struct { .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports), .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl, exports), .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports), + .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl, exports), } } @@ -461,6 +471,7 @@ pub const File = struct { .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl), .c => unreachable, .wasm => unreachable, + .spirv => unreachable, } } diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig new file mode 100644 index 0000000000..207e460174 --- /dev/null +++ b/src/link/SpirV.zig @@ -0,0 +1,131 @@ +const SpirV = @This(); + +const std = @import("std"); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; + +const Module = @import("../Module.zig"); +const Compilation = @import("../Compilation.zig"); +const link = @import("../link.zig"); +const codegen = @import("../codegen/spirv.zig"); +const trace = @import("../tracy.zig").trace; +const build_options = @import("build_options"); +const spec = @import("../codegen/spirv/spec.zig"); + +pub const FnData = struct { + id: ?u32 = null, + code: std.ArrayListUnmanaged(u32) = .{}, +}; + +base: link.File, + +// TODO: Does this file need to support multiple independent modules? +spirv_module: codegen.SPIRVModule = .{}, + +pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { + const spirv = try gpa.create(SpirV); + spirv.* = .{ + .base = .{ + .tag = .spirv, + .options = options, + .file = null, + .allocator = gpa, + }, + }; + return spirv; +} + +pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*SpirV { + assert(options.object_format == .spirv); + + if (options.use_llvm) return error.LLVM_BackendIsTODO_ForSpirV; // TODO: LLVM Doesn't support SpirV at all. + if (options.use_lld) return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all. + + // TODO: read the file and keep vaild parts instead of truncating + const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true }); + errdefer file.close(); + + const spirv = try createEmpty(allocator, options); + errdefer spirv.base.destroy(); + + spirv.base.file = file; + return spirv; +} + +pub fn deinit(self: *SpirV) void { +} + +pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const fn_data = &decl.fn_link.spirv; + if (fn_data.id == null) { + fn_data.id = self.spirv_module.allocId(); + } + + var managed_code = fn_data.code.toManaged(self.base.allocator); + managed_code.items.len = 0; + + try self.spirv_module.genDecl(fn_data.id.?, &managed_code, decl); + fn_data.code = managed_code.toUnmanaged(); + + // Free excess allocated memory for this Decl. + fn_data.code.shrinkAndFree(self.base.allocator, fn_data.code.items.len); +} + +pub fn updateDeclExports( + self: *SpirV, + module: *Module, + decl: *const Module.Decl, + exports: []const *Module.Export, +) !void {} + +pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void { + decl.fn_link.spirv.code.deinit(self.base.allocator); + decl.fn_link.spirv = undefined; +} + +pub fn flush(self: *SpirV, comp: *Compilation) !void { + if (build_options.have_llvm and self.base.options.use_lld) { + return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all. + } else { + return self.flushModule(comp); + } +} + +pub fn flushModule(self: *SpirV, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const module = self.base.options.module.?; + + const file = self.base.file.?; + var bw = std.io.bufferedWriter(file.writer()); + const writer = bw.writer(); + + // Header + // SPIR-V files support both little and big endian words. The actual format is disambiguated by + // the magic number. This backend uses little endian. + try writer.writeIntLittle(u32, spec.magic_number); + try writer.writeIntLittle(u32, (spec.version.major << 16) | (spec.version.minor) << 8); + try writer.writeIntLittle(u32, 0); // TODO: Register Zig compiler magic number. + try writer.writeIntLittle(u32, self.spirv_module.idBound()); + try writer.writeIntLittle(u32, 0); // Schema. + + // Declarations + for (module.decl_table.items()) |entry| { + const decl = entry.value; + switch (decl.typed_value) { + .most_recent => |tvm| { + const fn_data = &decl.fn_link.spirv; + for (fn_data.code.items) |word| { + try writer.writeIntLittle(u32, word); + } + }, + .never_succeeded => continue, + } + } + + try bw.flush(); +} diff --git a/src/main.zig b/src/main.zig index 13bea13a5e..a86ec4ccf5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -302,6 +302,7 @@ const usage_build_generic = \\ pe Portable Executable (Windows) \\ coff Common Object File Format (Windows) \\ macho macOS relocatables + \\ spirv Standard, Portable Intermediate Representation V (SPIR-V) \\ hex (planned) Intel IHEX \\ raw (planned) Dump machine code directly \\ -dirafter [dir] Add directory to AFTER include search path @@ -1515,6 +1516,8 @@ fn buildOutputType( break :blk .hex; } else if (mem.eql(u8, ofmt, "raw")) { break :blk .raw; + } else if (mem.eql(u8, ofmt, "spirv")) { + break :blk .spirv; } else { fatal("unsupported object format: {s}", .{ofmt}); } -- cgit v1.2.3 From 02c138fe7011346ebab5e4b24ba0f8575bb52173 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Mon, 18 Jan 2021 23:47:25 +0100 Subject: SPIR-V: Add glsl450 and vulkan spir-v operating system definitions --- lib/std/target.zig | 18 +++++++++++++++--- lib/std/zig/cross_target.zig | 4 ++++ src/codegen/llvm.zig | 5 +++++ src/link/SpirV.zig | 20 ++++++++++++++++++++ src/target.zig | 4 ++-- src/type.zig | 2 ++ 6 files changed, 48 insertions(+), 5 deletions(-) (limited to 'src/codegen') diff --git a/lib/std/target.zig b/lib/std/target.zig index b3e0f8afdd..66b5f560c1 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -57,7 +57,9 @@ pub const Target = struct { wasi, emscripten, uefi, - opencl, // SPIR-V on OpenCL + opencl, + glsl450, + vulkan, other, pub fn isDarwin(tag: Tag) bool { @@ -249,7 +251,9 @@ pub const Target = struct { .wasi, .emscripten, .uefi, - .opencl, + .opencl, // TODO: OpenCL versions + .glsl450, // TODO: GLSL versions + .vulkan, .other, => return .{ .none = {} }, @@ -406,6 +410,8 @@ pub const Target = struct { .emscripten, .uefi, .opencl, + .glsl450, + .vulkan, .other, => false, }; @@ -497,7 +503,9 @@ pub const Target = struct { .wasi, .emscripten, => return .musl, - .opencl, // TODO: Where should this go? + .opencl, // TODO: SPIR-V ABIs with Linkage capability + .glsl450, + .vulkan, => return .none, } } @@ -1367,6 +1375,8 @@ pub const Target = struct { .windows, .emscripten, .opencl, + .glsl450, + .vulkan, .other, => return false, else => return true, @@ -1547,6 +1557,8 @@ pub const Target = struct { .emscripten, .wasi, .opencl, + .glsl450, + .vulkan, .other, => return result, diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index 1e9066b90b..c34dcc2bd3 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -131,6 +131,8 @@ pub const CrossTarget = struct { .emscripten, .uefi, .opencl, + .glsl450, + .vulkan, .other, => { self.os_version_min = .{ .none = {} }; @@ -732,6 +734,8 @@ pub const CrossTarget = struct { .emscripten, .uefi, .opencl, + .glsl450, + .vulkan, .other, => return error.InvalidOperatingSystemVersion, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 1edd466d54..df6a58b1e2 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -69,6 +69,8 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { .renderscript64 => "renderscript64", .ve => "ve", .spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII, + .spirv32 => return error.LLVMBackendDoesNotSupportSPIRV, + .spirv64 => return error.LLVMBackendDoesNotSupportSPIRV, }; // TODO Add a sub-arch for some architectures depending on CPU features. @@ -109,6 +111,9 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { .wasi => "wasi", .emscripten => "emscripten", .uefi => "windows", + .opencl => return error.LLVMBackendDoesNotSupportOpenCL, + .glsl450 => return error.LLVMBackendDoesNotSupportGLSL450, + .vulkan => return error.LLVMBackendDoesNotSupportVulkan, .other => "unknown", }; diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 207e460174..68edfab845 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -12,6 +12,8 @@ const trace = @import("../tracy.zig").trace; const build_options = @import("build_options"); const spec = @import("../codegen/spirv/spec.zig"); +//! SPIR-V Documentation: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html + pub const FnData = struct { id: ?u32 = null, code: std.ArrayListUnmanaged(u32) = .{}, @@ -32,6 +34,22 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { .allocator = gpa, }, }; + + // TODO: Figure out where to put all of these + switch (options.target.cpu.arch) { + .spirv32, .spirv64 => {}, + else => return error.TODOArchNotSupported, + } + + switch (options.target.os.tag) { + .opencl, .glsl450, .vulkan => {}, + else => return error.TODOOsNotSupported, + } + + if (options.target.abi != .none) { + return error.TODOAbiNotSupported; + } + return spirv; } @@ -119,6 +137,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { switch (decl.typed_value) { .most_recent => |tvm| { const fn_data = &decl.fn_link.spirv; + + // TODO: This could probably be more efficient. for (fn_data.code.items) |word| { try writer.writeIntLittle(u32, word); } diff --git a/src/target.zig b/src/target.zig index c3df682ce0..e167520f89 100644 --- a/src/target.zig +++ b/src/target.zig @@ -189,7 +189,7 @@ pub fn supportsStackProbing(target: std.Target) bool { pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType { return switch (os_tag) { - .freestanding, .other => .UnknownOS, + .freestanding, .other, .opencl, .glsl450, .vulkan => .UnknownOS, .windows, .uefi => .Win32, .ananas => .Ananas, .cloudabi => .CloudABI, @@ -280,7 +280,7 @@ pub fn archToLLVM(arch_tag: std.Target.Cpu.Arch) llvm.ArchType { .renderscript32 => .renderscript32, .renderscript64 => .renderscript64, .ve => .ve, - .spu_2 => .UnknownArch, + .spu_2, .spirv32, .spirv64 => .UnknownArch, }; } diff --git a/src/type.zig b/src/type.zig index cb2448aa1a..e0190cdd37 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3598,6 +3598,8 @@ pub const CType = enum { .hermit, .hurd, .opencl, + .glsl450, + .vulkan, => @panic("TODO specify the C integer and float type sizes for this OS"), } } -- cgit v1.2.3 From 71ac82ecb028605545b67eaa50b34f4e2494de44 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Tue, 19 Jan 2021 01:29:01 +0100 Subject: SPIR-V: Make emitting binary more efficient --- src/codegen/spirv.zig | 6 ++++ src/link/SpirV.zig | 81 ++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 67 insertions(+), 20 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index f0ebd49e1d..7e41913625 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -19,4 +19,10 @@ pub const SPIRVModule = struct { pub fn genDecl(self: SPIRVModule, id: u32, code: *std.ArrayList(u32), decl: *Decl) !void { } + + pub fn writeInstruction(code: *std.ArrayList(u32), instr: spec.Opcode, args: []const u32) !void { + const word_count = @intCast(u32, args.len + 1); + try code.append((word_count << 16) | @enumToInt(instr)); + try code.appendSlice(args); + } }; diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 68edfab845..03acf3c31e 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -12,7 +12,23 @@ const trace = @import("../tracy.zig").trace; const build_options = @import("build_options"); const spec = @import("../codegen/spirv/spec.zig"); -//! SPIR-V Documentation: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html +//! SPIR-V Spec documentation: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html +//! According to above documentation, a SPIR-V module has the following logical layout: +//! Header. +//! OpCapability instructions. +//! OpExtension instructions. +//! OpExtInstImport instructions. +//! A single OpMemoryModel instruction. +//! All entry points, declared with OpEntryPoint instructions. +//! All execution-mode declarators; OpExecutionMode and OpExecutionModeId instructions. +//! Debug instructions: +//! - First, OpString, OpSourceExtension, OpSource, OpSourceContinued (no forward references). +//! - OpName and OpMemberName instructions. +//! - OpModuleProcessed instructions. +//! All annotation (decoration) instructions. +//! All type declaration instructions, constant instructions, global variable declarations, (preferrably) OpUndef instructions. +//! All function declarations without a body (extern functions presumably). +//! All regular functions. pub const FnData = struct { id: ?u32 = null, @@ -103,7 +119,6 @@ pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void { decl.fn_link.spirv.code.deinit(self.base.allocator); decl.fn_link.spirv = undefined; } - pub fn flush(self: *SpirV, comp: *Compilation) !void { if (build_options.have_llvm and self.base.options.use_lld) { return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all. @@ -118,34 +133,60 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { const module = self.base.options.module.?; - const file = self.base.file.?; - var bw = std.io.bufferedWriter(file.writer()); - const writer = bw.writer(); + var binary = std.ArrayList(u32).init(self.base.allocator); + defer binary.deinit(); // Header - // SPIR-V files support both little and big endian words. The actual format is disambiguated by - // the magic number. This backend uses little endian. - try writer.writeIntLittle(u32, spec.magic_number); - try writer.writeIntLittle(u32, (spec.version.major << 16) | (spec.version.minor) << 8); - try writer.writeIntLittle(u32, 0); // TODO: Register Zig compiler magic number. - try writer.writeIntLittle(u32, self.spirv_module.idBound()); - try writer.writeIntLittle(u32, 0); // Schema. - - // Declarations + { + const header = [_]u32{ + spec.magic_number, + (spec.version.major << 16) | (spec.version.minor << 8), + 0, // TODO: Register Zig compiler magic number. + self.spirv_module.idBound(), + 0, // Schema (currently reserved for future use in the SPIR-V spec). + }; + try binary.appendSlice(&header); + } + + // Collect list of buffers to write. + // SPIR-V files support both little and big endian words. The actual format is + // disambiguated by the magic number, and so theoretically we don't need to worry + // about endian-ness when writing the final binary. + var all_buffers = std.ArrayList(std.os.iovec_const).init(self.base.allocator); + defer all_buffers.deinit(); + + // Pre-allocate enough for the binary info + all functions + try all_buffers.ensureCapacity(module.decl_table.count() + 1); + + all_buffers.appendAssumeCapacity(wordsToIovConst(binary.items)); + + // Functions for (module.decl_table.items()) |entry| { const decl = entry.value; switch (decl.typed_value) { .most_recent => |tvm| { const fn_data = &decl.fn_link.spirv; - - // TODO: This could probably be more efficient. - for (fn_data.code.items) |word| { - try writer.writeIntLittle(u32, word); - } + all_buffers.appendAssumeCapacity(wordsToIovConst(fn_data.code.items)); }, .never_succeeded => continue, } } - try bw.flush(); + var file_size: u64 = 0; + for (all_buffers.items) |iov| { + file_size += iov.iov_len; + } + + const file = self.base.file.?; + try file.seekTo(0); + try file.setEndPos(file_size); + try file.pwritevAll(all_buffers.items, 0); +} + +fn wordsToIovConst(words: []const u32) std.os.iovec_const { + const bytes = std.mem.sliceAsBytes(words); + return .{ + .iov_base = bytes.ptr, + .iov_len = bytes.len, + }; } -- cgit v1.2.3 From 801732aebd3092c539b754170032455139c7418c Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Tue, 19 Jan 2021 01:54:01 +0100 Subject: SPIR-V: OpMemoryModel and basic capability generation --- src/codegen/spirv.zig | 12 +++++----- src/link/SpirV.zig | 61 +++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 55 insertions(+), 18 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 7e41913625..2fe759dc03 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -3,6 +3,12 @@ const spec = @import("spirv/spec.zig"); const Module = @import("../Module.zig"); const Decl = Module.Decl; +pub fn writeInstruction(code: *std.ArrayList(u32), instr: spec.Opcode, args: []const u32) !void { + const word_count = @intCast(u32, args.len + 1); + try code.append((word_count << 16) | @enumToInt(instr)); + try code.appendSlice(args); +} + pub const SPIRVModule = struct { // TODO: Also use a free list. next_id: u32 = 0, @@ -19,10 +25,4 @@ pub const SPIRVModule = struct { pub fn genDecl(self: SPIRVModule, id: u32, code: *std.ArrayList(u32), decl: *Decl) !void { } - - pub fn writeInstruction(code: *std.ArrayList(u32), instr: spec.Opcode, args: []const u32) !void { - const word_count = @intCast(u32, args.len + 1); - try code.append((word_count << 16) | @enumToInt(instr)); - try code.appendSlice(args); - } }; diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 03acf3c31e..80469726e0 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -132,21 +132,24 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { defer tracy.end(); const module = self.base.options.module.?; + const target = comp.getTarget(); var binary = std.ArrayList(u32).init(self.base.allocator); defer binary.deinit(); - // Header - { - const header = [_]u32{ - spec.magic_number, - (spec.version.major << 16) | (spec.version.minor << 8), - 0, // TODO: Register Zig compiler magic number. - self.spirv_module.idBound(), - 0, // Schema (currently reserved for future use in the SPIR-V spec). - }; - try binary.appendSlice(&header); - } + // Note: The order of adding functions to the final binary + // follows the SPIR-V logical moduel format! + + try binary.appendSlice(&[_]u32{ + spec.magic_number, + (spec.version.major << 16) | (spec.version.minor << 8), + 0, // TODO: Register Zig compiler magic number. + self.spirv_module.idBound(), + 0, // Schema (currently reserved for future use in the SPIR-V spec). + }); + + try writeCapabilities(&binary, target); + try writeMemoryModel(&binary, target); // Collect list of buffers to write. // SPIR-V files support both little and big endian words. The actual format is @@ -160,7 +163,6 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { all_buffers.appendAssumeCapacity(wordsToIovConst(binary.items)); - // Functions for (module.decl_table.items()) |entry| { const decl = entry.value; switch (decl.typed_value) { @@ -183,6 +185,41 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { try file.pwritevAll(all_buffers.items, 0); } +fn writeCapabilities(binary: *std.ArrayList(u32), target: std.Target) !void { + // TODO: Integrate with a hypothetical feature system + const cap: spec.Capability = switch (target.os.tag) { + .opencl => .Kernel, + .glsl450 => .Shader, + .vulkan => .VulkanMemoryModel, + else => unreachable, // TODO + }; + + try codegen.writeInstruction(binary, .OpCapability, &[_]u32{ @enumToInt(cap) }); +} + +fn writeMemoryModel(binary: *std.ArrayList(u32), target: std.Target) !void { + const addressing_model = switch (target.os.tag) { + .opencl => switch (target.cpu.arch) { + .spirv32 => spec.AddressingModel.Physical32, + .spirv64 => spec.AddressingModel.Physical64, + else => unreachable, // TODO + }, + .glsl450, .vulkan => spec.AddressingModel.Logical, + else => unreachable, // TODO + }; + + const memory_model: spec.MemoryModel = switch (target.os.tag) { + .opencl => .OpenCL, + .glsl450 => .GLSL450, + .vulkan => .Vulkan, + else => unreachable, + }; + + try codegen.writeInstruction(binary, .OpMemoryModel, &[_]u32{ + @enumToInt(addressing_model), @enumToInt(memory_model) + }); +} + fn wordsToIovConst(words: []const u32) std.os.iovec_const { const bytes = std.mem.sliceAsBytes(words); return .{ -- cgit v1.2.3 From 1055344673a87af39f2288bae069ec9403e6086d Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Tue, 19 Jan 2021 14:28:48 +0100 Subject: SPIR-V: Use free list for result id generation --- src/codegen/spirv.zig | 25 ++++++++++++++++++++++++- src/link/SpirV.zig | 13 +++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 2fe759dc03..5a262de836 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -1,4 +1,6 @@ const std = @import("std"); +const Allocator = std.mem.Allocator; + const spec = @import("spirv/spec.zig"); const Module = @import("../Module.zig"); const Decl = Module.Decl; @@ -10,14 +12,35 @@ pub fn writeInstruction(code: *std.ArrayList(u32), instr: spec.Opcode, args: []c } pub const SPIRVModule = struct { - // TODO: Also use a free list. next_id: u32 = 0, + free_id_list: std.ArrayList(u32), + + pub fn init(allocator: *Allocator) SPIRVModule { + return .{ + .free_id_list = std.ArrayList(u32).init(allocator), + }; + } + + pub fn deinit(self: *SPIRVModule) void { + self.free_id_list.deinit(); + } pub fn allocId(self: *SPIRVModule) u32 { + if (self.free_id_list.popOrNull()) |id| return id; + defer self.next_id += 1; return self.next_id; } + pub fn freeId(self: *SPIRVModule, id: u32) void { + if (id + 1 == self.next_id) { + self.next_id -= 1; + } else { + // If no more memory to append the id to the free list, just ignore it. + self.free_id_list.append(id) catch {}; + } + } + pub fn idBound(self: *SPIRVModule) u32 { return self.next_id; } diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 80469726e0..bde1eae391 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -38,7 +38,7 @@ pub const FnData = struct { base: link.File, // TODO: Does this file need to support multiple independent modules? -spirv_module: codegen.SPIRVModule = .{}, +spirv_module: codegen.SPIRVModule, pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { const spirv = try gpa.create(SpirV); @@ -49,6 +49,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { .file = null, .allocator = gpa, }, + .spirv_module = codegen.SPIRVModule.init(gpa), }; // TODO: Figure out where to put all of these @@ -87,6 +88,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio } pub fn deinit(self: *SpirV) void { + self.spirv_module.deinit(); } pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void { @@ -116,9 +118,12 @@ pub fn updateDeclExports( ) !void {} pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void { - decl.fn_link.spirv.code.deinit(self.base.allocator); + var fn_data = decl.fn_link.spirv; + fn_data.code.deinit(self.base.allocator); + if (fn_data.id) |id| self.spirv_module.freeId(id); decl.fn_link.spirv = undefined; } + pub fn flush(self: *SpirV, comp: *Compilation) !void { if (build_options.have_llvm and self.base.options.use_lld) { return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all. @@ -137,8 +142,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { var binary = std.ArrayList(u32).init(self.base.allocator); defer binary.deinit(); - // Note: The order of adding functions to the final binary - // follows the SPIR-V logical moduel format! + // Note: The order of adding sections to the final binary + // follows the SPIR-V logical module format! try binary.appendSlice(&[_]u32{ spec.magic_number, -- cgit v1.2.3 From a0d81caec99fe0d1cd803b0ba461b6e02829b476 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 24 Jan 2021 14:35:14 +0100 Subject: Nested conditions and loops support --- src/codegen/wasm.zig | 327 ++++++++++++++++++++++++++++++++++++++++----------- src/link/Wasm.zig | 5 +- 2 files changed, 263 insertions(+), 69 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index cbb2a42189..8acaebbd75 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -4,6 +4,7 @@ const ArrayList = std.ArrayList; const assert = std.debug.assert; const leb = std.leb; const mem = std.mem; +const wasm = std.wasm; const Module = @import("../Module.zig"); const Decl = Module.Decl; @@ -12,6 +13,7 @@ const Inst = ir.Inst; const Type = @import("../type.zig").Type; const Value = @import("../value.zig").Value; const Compilation = @import("../Compilation.zig"); +const AnyMCValue = @import("../codegen.zig").AnyMCValue; /// Wasm Value, created when generating an instruction const WValue = union(enum) { @@ -20,23 +22,14 @@ const WValue = union(enum) { local: u32, /// Instruction holding a constant `Value` constant: *Inst, - /// Block label + /// Offset position in the list of bytecode instructions + code_offset: usize, + /// The label of the block, used by breaks to find its relative distance block_idx: u32, }; /// Hashmap to store generated `WValue` for each `Inst` -pub const ValueTable = std.AutoHashMap(*Inst, WValue); - -/// Using a given `Type`, returns the corresponding wasm value type -fn genValtype(ty: Type) ?u8 { - return switch (ty.tag()) { - .f32 => 0x7D, - .f64 => 0x7C, - .u32, .i32 => 0x7F, - .u64, .i64 => 0x7E, - else => null, - }; -} +pub const ValueTable = std.AutoHashMapUnmanaged(*Inst, WValue); /// Code represents the `Code` section of wasm that /// belongs to a function @@ -58,13 +51,25 @@ pub const Context = struct { local_index: u32 = 0, /// If codegen fails, an error messages will be allocated and saved in `err_msg` err_msg: *Module.ErrorMsg, + /// Current block depth. Used to calculate the relative difference between a break + /// and block + block_depth: u32 = 0, + /// List of all locals' types generated throughout this declaration + /// used to emit locals count at start of 'code' section. + locals: std.ArrayListUnmanaged(u8), const InnerError = error{ OutOfMemory, CodegenFail, }; - /// Sets `err_msg` on `Context` and returns `error.CodegenFail` which is caught in link/Wasm.zig + pub fn deinit(self: *Context) void { + self.values.deinit(self.gpa); + self.locals.deinit(self.gpa); + self.* = undefined; + } + + /// Sets `err_msg` on `Context` and returns `error.CodegemFail` which is caught in link/Wasm.zig fn fail(self: *Context, src: usize, comptime fmt: []const u8, args: anytype) InnerError { self.err_msg = try Module.ErrorMsg.create(self.gpa, .{ .file_scope = self.decl.getFileScope(), @@ -85,13 +90,35 @@ pub const Context = struct { return self.values.get(inst).?; // Instruction does not dominate all uses! } + /// Using a given `Type`, returns the corresponding wasm value type + fn genValtype(self: *Context, src: usize, ty: Type) InnerError!u8 { + return switch (ty.tag()) { + .f32 => wasm.valtype(.f32), + .f64 => wasm.valtype(.f64), + .u32, .i32 => wasm.valtype(.i32), + .u64, .i64 => wasm.valtype(.i64), + else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}), + }; + } + + /// Using a given `Type`, returns the corresponding wasm value type + /// Differently from `genValtype` this also allows `void` to create a block + /// with no return type + fn genBlockType(self: *Context, src: usize, ty: Type) InnerError!u8 { + return switch (ty.tag()) { + .void, .noreturn => wasm.block_empty, + else => self.genValtype(src, ty), + }; + } + /// Writes the bytecode depending on the given `WValue` in `val` fn emitWValue(self: *Context, val: WValue) InnerError!void { const writer = self.code.writer(); switch (val) { - .none, .block_idx => {}, + .block_idx => unreachable, + .none, .code_offset => {}, .local => |idx| { - try writer.writeByte(0x20); // local.get + try writer.writeByte(wasm.opcode(.local_get)); try leb.writeULEB128(writer, idx); }, .constant => |inst| try self.emitConstant(inst.castTag(.constant).?), // creates a new constant onto the stack @@ -102,8 +129,7 @@ pub const Context = struct { const ty = self.decl.typed_value.most_recent.typed_value.ty; const writer = self.func_type_data.writer(); - // functype magic - try writer.writeByte(0x60); + try writer.writeByte(wasm.function_type); // param types try leb.writeULEB128(writer, @intCast(u32, ty.fnParamLen())); @@ -112,8 +138,8 @@ pub const Context = struct { defer self.gpa.free(params); ty.fnParamTypes(params); for (params) |param_type| { - const val_type = genValtype(param_type) orelse - return self.fail(self.decl.src(), "TODO: Wasm codegen - arg type value for type '{s}'", .{param_type.tag()}); + // Can we maybe get the source index of each param? + const val_type = try self.genValtype(self.decl.src(), param_type); try writer.writeByte(val_type); } } @@ -124,8 +150,8 @@ pub const Context = struct { .void, .noreturn => try leb.writeULEB128(writer, @as(u32, 0)), else => |ret_type| { try leb.writeULEB128(writer, @as(u32, 1)); - const val_type = genValtype(return_type) orelse - return self.fail(self.decl.src(), "TODO: Wasm codegen - return type value for type '{s}'", .{ret_type}); + // Can we maybe get the source index of the return type? + const val_type = try self.genValtype(self.decl.src(), return_type); try writer.writeByte(val_type); }, } @@ -140,37 +166,33 @@ pub const Context = struct { // Reserve space to write the size after generating the code try self.code.resize(5); + // offset into 'code' section where we will put our locals count + var local_offset = self.code.items.len; + // Write instructions // TODO: check for and handle death of instructions const tv = self.decl.typed_value.most_recent.typed_value; const mod_fn = tv.val.castTag(.function).?.data; + try self.genBody(mod_fn.body); - var locals = std.ArrayList(u8).init(self.gpa); - defer locals.deinit(); - - for (mod_fn.body.instructions) |inst| { - if (inst.tag != .alloc) continue; - - const alloc: *Inst.NoOp = inst.castTag(.alloc).?; - const elem_type = alloc.base.ty.elemType(); - - const wasm_type = genValtype(elem_type) orelse - return self.fail(inst.src, "TODO: Wasm codegen - valtype for type '{s}'", .{elem_type.tag()}); - - try locals.append(wasm_type); - } - - try leb.writeULEB128(writer, @intCast(u32, locals.items.len)); - - // emit the actual locals amount - for (locals.items) |local| { - try leb.writeULEB128(writer, @as(u32, 1)); - try leb.writeULEB128(writer, local); // valtype + // finally, write our local types at the 'offset' position + { + var totals_buffer: [5]u8 = undefined; + leb.writeUnsignedFixed(5, totals_buffer[0..5], @intCast(u32, self.locals.items.len)); + try self.code.insertSlice(local_offset, &totals_buffer); + local_offset += 5; + + // emit the actual locals amount + for (self.locals.items) |local| { + var buf: [6]u8 = undefined; + leb.writeUnsignedFixed(5, buf[0..5], @as(u32, 1)); + buf[5] = local; + try self.code.insertSlice(local_offset, &buf); + local_offset += 6; + } } - try self.genBody(mod_fn.body); - - try writer.writeByte(0x0B); // end + try writer.writeByte(wasm.opcode(.end)); // Fill in the size of the generated code to the reserved space at the // beginning of the buffer. @@ -183,10 +205,20 @@ pub const Context = struct { .add => self.genAdd(inst.castTag(.add).?), .alloc => self.genAlloc(inst.castTag(.alloc).?), .arg => self.genArg(inst.castTag(.arg).?), + .block => self.genBlock(inst.castTag(.block).?), + .br => self.genBr(inst.castTag(.br).?), .call => self.genCall(inst.castTag(.call).?), + .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq), + .cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte), + .cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt), + .cmp_lte => self.genCmp(inst.castTag(.cmp_lte).?, .lte), + .cmp_lt => self.genCmp(inst.castTag(.cmp_lt).?, .lt), + .cmp_neq => self.genCmp(inst.castTag(.cmp_neq).?, .neq), + .condbr => self.genCondBr(inst.castTag(.condbr).?), .constant => unreachable, .dbg_stmt => WValue.none, .load => self.genLoad(inst.castTag(.load).?), + .loop => self.genLoop(inst.castTag(.loop).?), .ret => self.genRet(inst.castTag(.ret).?), .retvoid => WValue.none, .store => self.genStore(inst.castTag(.store).?), @@ -197,7 +229,7 @@ pub const Context = struct { fn genBody(self: *Context, body: ir.Body) InnerError!void { for (body.instructions) |inst| { const result = try self.genInst(inst); - try self.values.putNoClobber(inst, result); + try self.values.putNoClobber(self.gpa, inst, result); } } @@ -205,7 +237,7 @@ pub const Context = struct { // TODO: Implement tail calls const operand = self.resolveInst(inst.operand); try self.emitWValue(operand); - return WValue.none; + return .none; } fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue { @@ -219,7 +251,7 @@ pub const Context = struct { try self.emitWValue(arg_val); } - try self.code.append(0x10); // call + try self.code.append(wasm.opcode(.call)); // The function index immediate argument will be filled in using this data // in link.Wasm.flush(). @@ -228,10 +260,14 @@ pub const Context = struct { .decl = target, }); - return WValue.none; + return .none; } fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue { + const elem_type = inst.base.ty.elemType(); + const valtype = try self.genValtype(inst.base.src, elem_type); + try self.locals.append(self.gpa, valtype); + defer self.local_index += 1; return WValue{ .local = self.local_index }; } @@ -243,15 +279,14 @@ pub const Context = struct { const rhs = self.resolveInst(inst.rhs); try self.emitWValue(rhs); - try writer.writeByte(0x21); // local.set + try writer.writeByte(wasm.opcode(.local_set)); try leb.writeULEB128(writer, lhs.local); - return WValue.none; + return .none; } fn genLoad(self: *Context, inst: *Inst.UnOp) InnerError!WValue { const operand = self.resolveInst(inst.operand); - try self.emitWValue(operand); - return WValue.none; + return operand; } fn genArg(self: *Context, inst: *Inst.Arg) InnerError!WValue { @@ -267,44 +302,44 @@ pub const Context = struct { try self.emitWValue(lhs); try self.emitWValue(rhs); - const opcode: u8 = switch (inst.base.ty.tag()) { - .u32, .i32 => 0x6A, //i32.add - .u64, .i64 => 0x7C, //i64.add - .f32 => 0x92, //f32.add - .f64 => 0xA0, //f64.add + const opcode: wasm.Opcode = switch (inst.base.ty.tag()) { + .u32, .i32 => .i32_add, + .u64, .i64 => .i64_add, + .f32 => .f32_add, + .f64 => .f64_add, else => return self.fail(inst.base.src, "TODO - Implement wasm genAdd for type '{s}'", .{inst.base.ty.tag()}), }; - try self.code.append(opcode); - return WValue.none; + try self.code.append(wasm.opcode(opcode)); + return .none; } fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void { const writer = self.code.writer(); switch (inst.base.ty.tag()) { .u32 => { - try writer.writeByte(0x41); // i32.const + try writer.writeByte(wasm.opcode(.i32_const)); try leb.writeILEB128(writer, inst.val.toUnsignedInt()); }, .i32 => { - try writer.writeByte(0x41); // i32.const + try writer.writeByte(wasm.opcode(.i32_const)); try leb.writeILEB128(writer, inst.val.toSignedInt()); }, .u64 => { - try writer.writeByte(0x42); // i64.const + try writer.writeByte(wasm.opcode(.i64_const)); try leb.writeILEB128(writer, inst.val.toUnsignedInt()); }, .i64 => { - try writer.writeByte(0x42); // i64.const + try writer.writeByte(wasm.opcode(.i64_const)); try leb.writeILEB128(writer, inst.val.toSignedInt()); }, .f32 => { - try writer.writeByte(0x43); // f32.const + try writer.writeByte(wasm.opcode(.f32_const)); // TODO: enforce LE byte order try writer.writeAll(mem.asBytes(&inst.val.toFloat(f32))); }, .f64 => { - try writer.writeByte(0x44); // f64.const + try writer.writeByte(wasm.opcode(.f64_const)); // TODO: enforce LE byte order try writer.writeAll(mem.asBytes(&inst.val.toFloat(f64))); }, @@ -312,4 +347,162 @@ pub const Context = struct { else => |ty| return self.fail(inst.base.src, "Wasm TODO: emitConstant for type {s}", .{ty}), } } + + fn genBlock(self: *Context, block: *Inst.Block) InnerError!WValue { + const block_ty = try self.genBlockType(block.base.src, block.base.ty); + + block.codegen = .{ + // we don't use relocs, so using `relocs` is illegal behaviour. + .relocs = undefined, + // Here we set the current block idx, so conditions know the depth to jump + // to when breaking out. This will be set to .none when it is found again within + // the same block + .mcv = @bitCast(AnyMCValue, WValue{ .block_idx = self.block_depth }), + }; + self.block_depth += 1; + + try self.code.append(wasm.opcode(.block)); + try self.code.append(block_ty); + try self.genBody(block.body); + try self.code.append(wasm.opcode(.end)); + + self.block_depth -= 1; + return .none; + } + + fn genLoop(self: *Context, loop: *Inst.Loop) InnerError!WValue { + const loop_ty = try self.genBlockType(loop.base.src, loop.base.ty); + + try self.code.append(wasm.opcode(.loop)); + try self.code.append(loop_ty); + self.block_depth += 1; + try self.genBody(loop.body); + self.block_depth -= 1; + + try self.code.append(wasm.opcode(.end)); + + return .none; + } + + fn genCondBr(self: *Context, condbr: *Inst.CondBr) InnerError!WValue { + const condition = self.resolveInst(condbr.condition); + const writer = self.code.writer(); + + // insert blocks at the position of `offset` so + // the condition can jump to it + const offset = condition.code_offset; + try self.code.insert(offset, wasm.opcode(.block)); + try self.code.insert(offset, try self.genBlockType(condbr.base.src, condbr.base.ty)); + + // we inserted the block in front of the condition + // so now check if condition matches. If not, break outside this block + // and continue with the regular codepath + try writer.writeByte(wasm.opcode(.br_if)); + try leb.writeULEB128(writer, @as(u32, 0)); + + // else body in case condition does not match + try self.genBody(condbr.else_body); + + // finally, tell wasm we have reached the end of the block we inserted above + try writer.writeByte(wasm.opcode(.end)); + + // Outer block that matches the condition + try self.genBody(condbr.then_body); + + return .none; + } + + fn genCmp(self: *Context, inst: *Inst.BinOp, op: std.math.CompareOperator) InnerError!WValue { + const ty = inst.lhs.ty.tag(); + + // save offset, so potential conditions can insert blocks in front of + // the comparison that we can later jump back to + const offset = self.code.items.len - 1; + + const lhs = self.resolveInst(inst.lhs); + const rhs = self.resolveInst(inst.rhs); + + try self.emitWValue(lhs); + try self.emitWValue(rhs); + + const opcode_maybe: ?wasm.Opcode = switch (op) { + .lt => @as(?wasm.Opcode, switch (ty) { + .i32 => .i32_lt_s, + .u32 => .i32_lt_u, + .i64 => .i64_lt_s, + .u64 => .i64_lt_u, + .f32 => .f32_lt, + .f64 => .f64_lt, + else => null, + }), + .lte => @as(?wasm.Opcode, switch (ty) { + .i32 => .i32_le_s, + .u32 => .i32_le_u, + .i64 => .i64_le_s, + .u64 => .i64_le_u, + .f32 => .f32_le, + .f64 => .f64_le, + else => null, + }), + .eq => @as(?wasm.Opcode, switch (ty) { + .i32, .u32 => .i32_eq, + .i64, .u64 => .i64_eq, + .f32 => .f32_eq, + .f64 => .f64_eq, + else => null, + }), + .gte => @as(?wasm.Opcode, switch (ty) { + .i32 => .i32_ge_s, + .u32 => .i32_ge_u, + .i64 => .i64_ge_s, + .u64 => .i64_ge_u, + .f32 => .f32_ge, + .f64 => .f64_ge, + else => null, + }), + .gt => @as(?wasm.Opcode, switch (ty) { + .i32 => .i32_gt_s, + .u32 => .i32_gt_u, + .i64 => .i64_gt_s, + .u64 => .i64_gt_u, + .f32 => .f32_gt, + .f64 => .f64_gt, + else => null, + }), + .neq => @as(?wasm.Opcode, switch (ty) { + .i32, .u32 => .i32_ne, + .i64, .u64 => .i64_ne, + .f32 => .f32_ne, + .f64 => .f64_ne, + else => null, + }), + }; + + const opcode = opcode_maybe orelse + return self.fail(inst.base.src, "TODO - Wasm genCmp for type '{s}' and operator '{s}'", .{ ty, @tagName(op) }); + + try self.code.append(wasm.opcode(opcode)); + return WValue{ .code_offset = offset }; + } + + fn genBr(self: *Context, br: *Inst.Br) InnerError!WValue { + // of operand has codegen bits we should break with a value + if (br.operand.ty.hasCodeGenBits()) { + const operand = self.resolveInst(br.operand); + try self.emitWValue(operand); + } + + // if the block contains a block_idx, do a relative jump to it + // if `wvalue` was already 'consumed', simply break out of current block + const wvalue = @bitCast(WValue, br.block.codegen.mcv); + const idx: u32 = if (wvalue == .block_idx) blk: { + br.block.codegen.mcv = @bitCast(AnyMCValue, WValue{ .none = {} }); + break :blk self.block_depth - wvalue.block_idx; + } else 0; + + const writer = self.code.writer(); + try writer.writeByte(wasm.opcode(.br)); + try leb.writeULEB128(writer, idx); + return WValue.none; + } }; diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index ec5ca0b9cf..1001e616e2 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -103,13 +103,14 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { var context = codegen.Context{ .gpa = self.base.allocator, - .values = codegen.ValueTable.init(self.base.allocator), + .values = .{}, .code = managed_code, .func_type_data = managed_functype, .decl = decl, .err_msg = undefined, + .locals = .{}, }; - defer context.values.deinit(); + defer context.deinit(); // generate the 'code' section for the function declaration context.gen() catch |err| switch (err) { -- cgit v1.2.3 From cc46c1b9024beefdd82ce8abd07e8849a72db20c Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 26 Jan 2021 19:47:15 +0100 Subject: Add tests, fix locals that are created in blocks like loops, and handle all breaks correctly --- src/codegen/wasm.zig | 86 +++++++++++++++++++++++++----------------------- src/link/Wasm.zig | 9 ++++- test/stage2/wasm.zig | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 41 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index 8acaebbd75..abc411b551 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -163,11 +163,8 @@ pub const Context = struct { try self.genFunctype(); const writer = self.code.writer(); - // Reserve space to write the size after generating the code - try self.code.resize(5); - - // offset into 'code' section where we will put our locals count - var local_offset = self.code.items.len; + // Reserve space to write the size after generating the code as well as space for locals count + try self.code.resize(10); // Write instructions // TODO: check for and handle death of instructions @@ -177,10 +174,10 @@ pub const Context = struct { // finally, write our local types at the 'offset' position { - var totals_buffer: [5]u8 = undefined; - leb.writeUnsignedFixed(5, totals_buffer[0..5], @intCast(u32, self.locals.items.len)); - try self.code.insertSlice(local_offset, &totals_buffer); - local_offset += 5; + leb.writeUnsignedFixed(5, self.code.items[5..10], @intCast(u32, self.locals.items.len)); + + // offset into 'code' section where we will put our locals types + var local_offset: usize = 10; // emit the actual locals amount for (self.locals.items) |local| { @@ -285,8 +282,7 @@ pub const Context = struct { } fn genLoad(self: *Context, inst: *Inst.UnOp) InnerError!WValue { - const operand = self.resolveInst(inst.operand); - return operand; + return self.resolveInst(inst.operand); } fn genArg(self: *Context, inst: *Inst.Arg) InnerError!WValue { @@ -351,35 +347,49 @@ pub const Context = struct { fn genBlock(self: *Context, block: *Inst.Block) InnerError!WValue { const block_ty = try self.genBlockType(block.base.src, block.base.ty); + try self.startBlock(.block, block_ty, null); block.codegen = .{ // we don't use relocs, so using `relocs` is illegal behaviour. .relocs = undefined, - // Here we set the current block idx, so conditions know the depth to jump - // to when breaking out. This will be set to .none when it is found again within - // the same block + // Here we set the current block idx, so breaks know the depth to jump + // to when breaking out. .mcv = @bitCast(AnyMCValue, WValue{ .block_idx = self.block_depth }), }; + try self.genBody(block.body); + try self.endBlock(); + + return .none; + } + + /// appends a new wasm block to the code section and increases the `block_depth` by 1 + fn startBlock(self: *Context, block_type: wasm.Opcode, valtype: u8, with_offset: ?usize) !void { self.block_depth += 1; + if (with_offset) |offset| { + try self.code.insert(offset, wasm.opcode(block_type)); + try self.code.insert(offset + 1, valtype); + } else { + try self.code.append(wasm.opcode(block_type)); + try self.code.append(valtype); + } + } - try self.code.append(wasm.opcode(.block)); - try self.code.append(block_ty); - try self.genBody(block.body); + /// Ends the current wasm block and decreases the `block_depth` by 1 + fn endBlock(self: *Context) !void { try self.code.append(wasm.opcode(.end)); - self.block_depth -= 1; - return .none; } fn genLoop(self: *Context, loop: *Inst.Loop) InnerError!WValue { const loop_ty = try self.genBlockType(loop.base.src, loop.base.ty); - try self.code.append(wasm.opcode(.loop)); - try self.code.append(loop_ty); - self.block_depth += 1; + try self.startBlock(.loop, loop_ty, null); try self.genBody(loop.body); - self.block_depth -= 1; - try self.code.append(wasm.opcode(.end)); + // breaking to the index of a loop block will continue the loop instead + try self.code.append(wasm.opcode(.br)); + try leb.writeULEB128(self.code.writer(), @as(u32, 0)); + + try self.endBlock(); return .none; } @@ -388,23 +398,22 @@ pub const Context = struct { const condition = self.resolveInst(condbr.condition); const writer = self.code.writer(); + // TODO: Handle death instructions for then and else body + // insert blocks at the position of `offset` so // the condition can jump to it const offset = condition.code_offset; - try self.code.insert(offset, wasm.opcode(.block)); - try self.code.insert(offset, try self.genBlockType(condbr.base.src, condbr.base.ty)); + const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty); + try self.startBlock(.block, block_ty, offset); // we inserted the block in front of the condition // so now check if condition matches. If not, break outside this block - // and continue with the regular codepath + // and continue with the then codepath try writer.writeByte(wasm.opcode(.br_if)); try leb.writeULEB128(writer, @as(u32, 0)); - // else body in case condition does not match try self.genBody(condbr.else_body); - - // finally, tell wasm we have reached the end of the block we inserted above - try writer.writeByte(wasm.opcode(.end)); + try self.endBlock(); // Outer block that matches the condition try self.genBody(condbr.then_body); @@ -417,7 +426,7 @@ pub const Context = struct { // save offset, so potential conditions can insert blocks in front of // the comparison that we can later jump back to - const offset = self.code.items.len - 1; + const offset = self.code.items.len; const lhs = self.resolveInst(inst.lhs); const rhs = self.resolveInst(inst.rhs); @@ -492,17 +501,14 @@ pub const Context = struct { try self.emitWValue(operand); } - // if the block contains a block_idx, do a relative jump to it - // if `wvalue` was already 'consumed', simply break out of current block + // every block contains a `WValue` with its block index. + // We then determine how far we have to jump to it by substracting it from current block depth const wvalue = @bitCast(WValue, br.block.codegen.mcv); - const idx: u32 = if (wvalue == .block_idx) blk: { - br.block.codegen.mcv = @bitCast(AnyMCValue, WValue{ .none = {} }); - break :blk self.block_depth - wvalue.block_idx; - } else 0; - + const idx: u32 = self.block_depth - wvalue.block_idx; const writer = self.code.writer(); try writer.writeByte(wasm.opcode(.br)); try leb.writeULEB128(writer, idx); - return WValue.none; + + return .none; } }; diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 1001e616e2..c39e995966 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -122,6 +122,13 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { else => |e| return err, }; + // as locals are patched afterwards, the offsets of funcidx's are off, + // here we update them to correct them + for (decl.fn_link.wasm.?.idx_refs.items) |*func| { + // For each local, add 6 bytes (count + type) + func.offset += @intCast(u32, context.locals.items.len * 6); + } + fn_data.functype = context.func_type_data.toUnmanaged(); fn_data.code = context.code.toUnmanaged(); } @@ -238,7 +245,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { try writer.writeAll(fn_data.code.items[current..idx_ref.offset]); current = idx_ref.offset; // Use a fixed width here to make calculating the code size - // in codegen.wasm.genCode() simpler. + // in codegen.wasm.gen() simpler. var buf: [5]u8 = undefined; leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?); try writer.writeAll(&buf); diff --git a/test/stage2/wasm.zig b/test/stage2/wasm.zig index f522db8809..06ede2d735 100644 --- a/test/stage2/wasm.zig +++ b/test/stage2/wasm.zig @@ -122,4 +122,96 @@ pub fn addCases(ctx: *TestContext) !void { \\} , "35\n"); } + + { + var case = ctx.exe("wasm conditions", wasi); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 5; + \\ if (i > @as(u32, 4)) { + \\ i += 10; + \\ } + \\ return i; + \\} + , "15\n"); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 5; + \\ if (i < @as(u32, 4)) { + \\ i += 10; + \\ } else { + \\ i = 2; + \\ } + \\ return i; + \\} + , "2\n"); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 5; + \\ if (i < @as(u32, 4)) { + \\ i += 10; + \\ } else if(i == @as(u32, 5)) { + \\ i = 20; + \\ } + \\ return i; + \\} + , "20\n"); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 11; + \\ if (i < @as(u32, 4)) { + \\ i += 10; + \\ } else { + \\ if (i > @as(u32, 10)) { + \\ i += 20; + \\ } else { + \\ i = 20; + \\ } + \\ } + \\ return i; + \\} + , "31\n"); + } + + { + var case = ctx.exe("wasm while loops", wasi); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 0; + \\ while(i < @as(u32, 5)){ + \\ i += 1; + \\ } + \\ + \\ return i; + \\} + , "5\n"); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 0; + \\ while(i < @as(u32, 10)){ + \\ var x: u32 = 1; + \\ i += x; + \\ } + \\ return i; + \\} + , "10\n"); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 0; + \\ while(i < @as(u32, 10)){ + \\ var x: u32 = 1; + \\ i += x; + \\ if (i == @as(u32, 5)) break; + \\ } + \\ return i; + \\} + , "5\n"); + } } -- cgit v1.2.3 From 81c512f35b1926cf3fb6f29b97e68256aa164f68 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 26 Jan 2021 19:50:46 +0200 Subject: stage2 cbe: loop instruction --- src/codegen/c.zig | 99 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 41 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/c.zig b/src/codegen/c.zig index b26f753757..ccde36a10d 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -6,7 +6,8 @@ const Writer = std.ArrayList(u8).Writer; const link = @import("../link.zig"); const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); -const Inst = @import("../ir.zig").Inst; +const ir = @import("../ir.zig"); +const Inst = ir.Inst; const Value = @import("../value.zig").Value; const Type = @import("../type.zig").Type; const TypedValue = @import("../TypedValue.zig"); @@ -324,51 +325,13 @@ pub fn genDecl(o: *Object) !void { try fwd_decl_writer.writeAll(";\n"); const func: *Module.Fn = func_payload.data; - const instructions = func.body.instructions; const writer = o.code.writer(); try writer.writeAll("\n"); try o.dg.renderFunctionSignature(writer, is_global); - if (instructions.len == 0) { - try writer.writeAll(" {}\n"); - return; - } - - try writer.writeAll(" {"); + + try genBody(o, func.body); try writer.writeAll("\n"); - for (instructions) |inst| { - const result_value = switch (inst.tag) { - .add => try genBinOp(o, inst.castTag(.add).?, " + "), - .alloc => try genAlloc(o, inst.castTag(.alloc).?), - .arg => genArg(o), - .assembly => try genAsm(o, inst.castTag(.assembly).?), - .block => try genBlock(o, inst.castTag(.block).?), - .bitcast => try genBitcast(o, inst.castTag(.bitcast).?), - .breakpoint => try genBreakpoint(o, inst.castTag(.breakpoint).?), - .call => try genCall(o, inst.castTag(.call).?), - .cmp_eq => try genBinOp(o, inst.castTag(.cmp_eq).?, " == "), - .cmp_gt => try genBinOp(o, inst.castTag(.cmp_gt).?, " > "), - .cmp_gte => try genBinOp(o, inst.castTag(.cmp_gte).?, " >= "), - .cmp_lt => try genBinOp(o, inst.castTag(.cmp_lt).?, " < "), - .cmp_lte => try genBinOp(o, inst.castTag(.cmp_lte).?, " <= "), - .cmp_neq => try genBinOp(o, inst.castTag(.cmp_neq).?, " != "), - .dbg_stmt => try genDbgStmt(o, inst.castTag(.dbg_stmt).?), - .intcast => try genIntCast(o, inst.castTag(.intcast).?), - .load => try genLoad(o, inst.castTag(.load).?), - .ret => try genRet(o, inst.castTag(.ret).?), - .retvoid => try genRetVoid(o), - .store => try genStore(o, inst.castTag(.store).?), - .sub => try genBinOp(o, inst.castTag(.sub).?, " - "), - .unreach => try genUnreach(o, inst.castTag(.unreach).?), - else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), - }; - switch (result_value) { - .none => {}, - else => try o.value_map.putNoClobber(inst, result_value), - } - } - - try writer.writeAll("}\n"); } else if (tv.val.tag() == .extern_fn) { const writer = o.code.writer(); try writer.writeAll("ZIG_EXTERN_C "); @@ -410,6 +373,52 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { } } +pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!void { + const writer = o.code.writer(); + if (body.instructions.len == 0) { + try writer.writeAll(" {}"); + return; + } + + try writer.writeAll(" {"); + + try writer.writeAll("\n"); + for (body.instructions) |inst| { + const result_value = switch (inst.tag) { + .add => try genBinOp(o, inst.castTag(.add).?, " + "), + .alloc => try genAlloc(o, inst.castTag(.alloc).?), + .arg => genArg(o), + .assembly => try genAsm(o, inst.castTag(.assembly).?), + .block => try genBlock(o, inst.castTag(.block).?), + .bitcast => try genBitcast(o, inst.castTag(.bitcast).?), + .breakpoint => try genBreakpoint(o, inst.castTag(.breakpoint).?), + .call => try genCall(o, inst.castTag(.call).?), + .cmp_eq => try genBinOp(o, inst.castTag(.cmp_eq).?, " == "), + .cmp_gt => try genBinOp(o, inst.castTag(.cmp_gt).?, " > "), + .cmp_gte => try genBinOp(o, inst.castTag(.cmp_gte).?, " >= "), + .cmp_lt => try genBinOp(o, inst.castTag(.cmp_lt).?, " < "), + .cmp_lte => try genBinOp(o, inst.castTag(.cmp_lte).?, " <= "), + .cmp_neq => try genBinOp(o, inst.castTag(.cmp_neq).?, " != "), + .dbg_stmt => try genDbgStmt(o, inst.castTag(.dbg_stmt).?), + .intcast => try genIntCast(o, inst.castTag(.intcast).?), + .load => try genLoad(o, inst.castTag(.load).?), + .ret => try genRet(o, inst.castTag(.ret).?), + .retvoid => try genRetVoid(o), + .store => try genStore(o, inst.castTag(.store).?), + .sub => try genBinOp(o, inst.castTag(.sub).?, " - "), + .unreach => try genUnreach(o, inst.castTag(.unreach).?), + .loop => try genLoop(o, inst.castTag(.loop).?), + else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), + }; + switch (result_value) { + .none => {}, + else => try o.value_map.putNoClobber(inst, result_value), + } + } + + try writer.writeAll("}"); +} + fn genAlloc(o: *Object, alloc: *Inst.NoOp) !CValue { const writer = o.code.writer(); @@ -627,6 +636,14 @@ fn genUnreach(o: *Object, inst: *Inst.NoOp) !CValue { return CValue.none; } +fn genLoop(o: *Object, inst: *Inst.Loop) !CValue { + try o.indent(); + try o.code.writer().writeAll("while (true)"); + try genBody(o, inst.body); + try o.code.writer().writeAll("\n"); + return CValue.none; +} + fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { if (as.base.isUnused() and !as.is_volatile) return CValue.none; -- cgit v1.2.3 From 6ca0ff90b63cea79d8d63519a3c133cfde111884 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 26 Jan 2021 20:25:30 +0200 Subject: stage2 cbe: use AutoIndentingStream --- src/codegen/c.zig | 100 ++++++++++++++++++++++-------------------------------- src/link/C.zig | 2 ++ 2 files changed, 43 insertions(+), 59 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/c.zig b/src/codegen/c.zig index ccde36a10d..9bccde5ffd 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1,7 +1,6 @@ const std = @import("std"); const mem = std.mem; const log = std.log.scoped(.c); -const Writer = std.ArrayList(u8).Writer; const link = @import("../link.zig"); const Module = @import("../Module.zig"); @@ -42,6 +41,7 @@ pub const Object = struct { value_map: CValueMap, next_arg_index: usize = 0, next_local_index: usize = 0, + indent_writer: std.io.AutoIndentingStream(std.ArrayList(u8).Writer), fn resolveInst(o: *Object, inst: *Inst) !CValue { if (inst.value()) |_| { @@ -58,31 +58,28 @@ pub const Object = struct { fn allocLocal(o: *Object, ty: Type, mutability: Mutability) !CValue { const local_value = o.allocLocalValue(); - try o.renderTypeAndName(o.code.writer(), ty, local_value, mutability); + try o.renderTypeAndName(o.writer(), ty, local_value, mutability); return local_value; } - fn indent(o: *Object) !void { - const indent_size = 4; - const indent_level = 1; - const indent_amt = indent_size * indent_level; - try o.code.writer().writeByteNTimes(' ', indent_amt); + fn writer(o: *Object) std.io.AutoIndentingStream(std.ArrayList(u8).Writer).Writer { + return o.indent_writer.writer(); } - fn writeCValue(o: *Object, writer: Writer, c_value: CValue) !void { + fn writeCValue(o: *Object, w: anytype, c_value: CValue) !void { switch (c_value) { .none => unreachable, - .local => |i| return writer.print("t{d}", .{i}), - .local_ref => |i| return writer.print("&t{d}", .{i}), - .constant => |inst| return o.dg.renderValue(writer, inst.ty, inst.value().?), - .arg => |i| return writer.print("a{d}", .{i}), - .decl => |decl| return writer.writeAll(mem.span(decl.name)), + .local => |i| return w.print("t{d}", .{i}), + .local_ref => |i| return w.print("&t{d}", .{i}), + .constant => |inst| return o.dg.renderValue(w, inst.ty, inst.value().?), + .arg => |i| return w.print("a{d}", .{i}), + .decl => |decl| return w.writeAll(mem.span(decl.name)), } } fn renderTypeAndName( o: *Object, - writer: Writer, + w: anytype, ty: Type, name: CValue, mutability: Mutability, @@ -98,15 +95,15 @@ pub const Object = struct { render_ty = render_ty.elemType(); } - try o.dg.renderType(writer, render_ty); + try o.dg.renderType(w, render_ty); const const_prefix = switch (mutability) { .Const => "const ", .Mut => "", }; - try writer.print(" {s}", .{const_prefix}); - try o.writeCValue(writer, name); - try writer.writeAll(suffix.items); + try w.print(" {s}", .{const_prefix}); + try o.writeCValue(w, name); + try w.writeAll(suffix.items); } }; @@ -127,7 +124,7 @@ pub const DeclGen = struct { fn renderValue( dg: *DeclGen, - writer: Writer, + writer: anytype, t: Type, val: Value, ) error{ OutOfMemory, AnalysisFail }!void { @@ -204,7 +201,7 @@ pub const DeclGen = struct { } } - fn renderFunctionSignature(dg: *DeclGen, w: Writer, is_global: bool) !void { + fn renderFunctionSignature(dg: *DeclGen, w: anytype, is_global: bool) !void { if (!is_global) { try w.writeAll("static "); } @@ -228,7 +225,7 @@ pub const DeclGen = struct { try w.writeByte(')'); } - fn renderType(dg: *DeclGen, w: Writer, t: Type) error{ OutOfMemory, AnalysisFail }!void { + fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void { switch (t.zigTypeTag()) { .NoReturn => { try w.writeAll("zig_noreturn void"); @@ -325,20 +322,19 @@ pub fn genDecl(o: *Object) !void { try fwd_decl_writer.writeAll(";\n"); const func: *Module.Fn = func_payload.data; - const writer = o.code.writer(); - try writer.writeAll("\n"); - try o.dg.renderFunctionSignature(writer, is_global); + try o.indent_writer.insertNewline(); + try o.dg.renderFunctionSignature(o.writer(), is_global); try genBody(o, func.body); - try writer.writeAll("\n"); + try o.indent_writer.insertNewline(); } else if (tv.val.tag() == .extern_fn) { - const writer = o.code.writer(); + const writer = o.writer(); try writer.writeAll("ZIG_EXTERN_C "); try o.dg.renderFunctionSignature(writer, true); try writer.writeAll(";\n"); } else { - const writer = o.code.writer(); + const writer = o.writer(); try writer.writeAll("static "); // TODO ask the Decl if it is const @@ -374,15 +370,15 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { } pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!void { - const writer = o.code.writer(); + const writer = o.writer(); if (body.instructions.len == 0) { try writer.writeAll(" {}"); return; } - try writer.writeAll(" {"); + try writer.writeAll(" {\n"); + o.indent_writer.pushIndent(); - try writer.writeAll("\n"); for (body.instructions) |inst| { const result_value = switch (inst.tag) { .add => try genBinOp(o, inst.castTag(.add).?, " + "), @@ -416,14 +412,14 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi } } + o.indent_writer.popIndent(); try writer.writeAll("}"); } fn genAlloc(o: *Object, alloc: *Inst.NoOp) !CValue { - const writer = o.code.writer(); + const writer = o.writer(); // First line: the variable used as data storage. - try o.indent(); const elem_type = alloc.base.ty.elemType(); const mutability: Mutability = if (alloc.base.ty.isConstPtr()) .Const else .Mut; const local = try o.allocLocal(elem_type, mutability); @@ -439,15 +435,13 @@ fn genArg(o: *Object) CValue { } fn genRetVoid(o: *Object) !CValue { - try o.indent(); - try o.code.writer().print("return;\n", .{}); + try o.writer().print("return;\n", .{}); return CValue.none; } fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue { const operand = try o.resolveInst(inst.operand); - const writer = o.code.writer(); - try o.indent(); + const writer = o.writer(); const local = try o.allocLocal(inst.base.ty, .Const); switch (operand) { .local_ref => |i| { @@ -467,8 +461,7 @@ fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue { fn genRet(o: *Object, inst: *Inst.UnOp) !CValue { const operand = try o.resolveInst(inst.operand); - try o.indent(); - const writer = o.code.writer(); + const writer = o.writer(); try writer.writeAll("return "); try o.writeCValue(writer, operand); try writer.writeAll(";\n"); @@ -481,8 +474,7 @@ fn genIntCast(o: *Object, inst: *Inst.UnOp) !CValue { const from = try o.resolveInst(inst.operand); - try o.indent(); - const writer = o.code.writer(); + const writer = o.writer(); const local = try o.allocLocal(inst.base.ty, .Const); try writer.writeAll(" = ("); try o.dg.renderType(writer, inst.base.ty); @@ -497,8 +489,7 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue { const dest_ptr = try o.resolveInst(inst.lhs); const src_val = try o.resolveInst(inst.rhs); - try o.indent(); - const writer = o.code.writer(); + const writer = o.writer(); switch (dest_ptr) { .local_ref => |i| { const dest: CValue = .{ .local = i }; @@ -525,8 +516,7 @@ fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue { const lhs = try o.resolveInst(inst.lhs); const rhs = try o.resolveInst(inst.rhs); - try o.indent(); - const writer = o.code.writer(); + const writer = o.writer(); const local = try o.allocLocal(inst.base.ty, .Const); try writer.writeAll(" = "); @@ -552,8 +542,7 @@ fn genCall(o: *Object, inst: *Inst.Call) !CValue { const unused_result = inst.base.isUnused(); var result_local: CValue = .none; - try o.indent(); - const writer = o.code.writer(); + const writer = o.writer(); if (unused_result) { if (ret_ty.hasCodeGenBits()) { try writer.print("(void)", .{}); @@ -596,8 +585,7 @@ fn genBlock(o: *Object, inst: *Inst.Block) !CValue { fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue { const operand = try o.resolveInst(inst.operand); - const writer = o.code.writer(); - try o.indent(); + const writer = o.writer(); if (inst.base.ty.zigTypeTag() == .Pointer and inst.operand.ty.zigTypeTag() == .Pointer) { const local = try o.allocLocal(inst.base.ty, .Const); try writer.writeAll(" = ("); @@ -611,7 +599,6 @@ fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue { const local = try o.allocLocal(inst.base.ty, .Mut); try writer.writeAll(";\n"); - try o.indent(); try writer.writeAll("memcpy(&"); try o.writeCValue(writer, local); @@ -625,22 +612,19 @@ fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue { } fn genBreakpoint(o: *Object, inst: *Inst.NoOp) !CValue { - try o.indent(); - try o.code.writer().writeAll("zig_breakpoint();\n"); + try o.writer().writeAll("zig_breakpoint();\n"); return CValue.none; } fn genUnreach(o: *Object, inst: *Inst.NoOp) !CValue { - try o.indent(); - try o.code.writer().writeAll("zig_unreachable();\n"); + try o.writer().writeAll("zig_unreachable();\n"); return CValue.none; } fn genLoop(o: *Object, inst: *Inst.Loop) !CValue { - try o.indent(); - try o.code.writer().writeAll("while (true)"); + try o.writer().writeAll("while (true)"); try genBody(o, inst.body); - try o.code.writer().writeAll("\n"); + try o.indent_writer.insertNewline(); return CValue.none; } @@ -648,13 +632,12 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { if (as.base.isUnused() and !as.is_volatile) return CValue.none; - const writer = o.code.writer(); + const writer = o.writer(); for (as.inputs) |i, index| { if (i[0] == '{' and i[i.len - 1] == '}') { const reg = i[1 .. i.len - 1]; const arg = as.args[index]; const arg_c_value = try o.resolveInst(arg); - try o.indent(); try writer.writeAll("register "); try o.dg.renderType(writer, arg.ty); @@ -665,7 +648,6 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { return o.dg.fail(o.dg.decl.src(), "TODO non-explicit inline asm regs", .{}); } } - try o.indent(); const volatile_string: []const u8 = if (as.is_volatile) "volatile " else ""; try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source }); if (as.output) |_| { diff --git a/src/link/C.zig b/src/link/C.zig index a60d0efd8e..765249cd7d 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -95,7 +95,9 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { .gpa = module.gpa, .code = code.toManaged(module.gpa), .value_map = codegen.CValueMap.init(module.gpa), + .indent_writer = undefined, // set later so we can get a pointer to object.code }; + object.indent_writer = std.io.autoIndentingStream(4, object.code.writer()); defer object.value_map.deinit(); defer object.code.deinit(); defer object.dg.fwd_decl.deinit(); -- cgit v1.2.3 From bdfe3aeab8310a64cc9c2f5fac194a609aa0f13d Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 26 Jan 2021 21:09:40 +0200 Subject: stage2 cbe: condbr and breaks --- src/codegen/c.zig | 56 +++++++++++++++++++++++++++++++++++++++++++++++------ test/stage2/cbe.zig | 18 +++++++++++++++++ 2 files changed, 68 insertions(+), 6 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 9bccde5ffd..bf6a5aac1f 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -41,6 +41,7 @@ pub const Object = struct { value_map: CValueMap, next_arg_index: usize = 0, next_local_index: usize = 0, + next_block_index: usize = 0, indent_writer: std.io.AutoIndentingStream(std.ArrayList(u8).Writer), fn resolveInst(o: *Object, inst: *Inst) !CValue { @@ -255,8 +256,8 @@ pub const DeclGen = struct { .int_signed, .int_unsigned => { const info = t.intInfo(dg.module.getTarget()); const sign_prefix = switch (info.signedness) { - .signed => "i", - .unsigned => "", + .signed => "", + .unsigned => "u", }; inline for (.{ 8, 16, 32, 64, 128 }) |nbits| { if (info.bits <= nbits) { @@ -325,6 +326,7 @@ pub fn genDecl(o: *Object) !void { try o.indent_writer.insertNewline(); try o.dg.renderFunctionSignature(o.writer(), is_global); + try o.writer().writeByte(' '); try genBody(o, func.body); try o.indent_writer.insertNewline(); @@ -372,11 +374,11 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!void { const writer = o.writer(); if (body.instructions.len == 0) { - try writer.writeAll(" {}"); + try writer.writeAll("{}"); return; } - try writer.writeAll(" {\n"); + try writer.writeAll("{\n"); o.indent_writer.pushIndent(); for (body.instructions) |inst| { @@ -404,6 +406,9 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi .sub => try genBinOp(o, inst.castTag(.sub).?, " - "), .unreach => try genUnreach(o, inst.castTag(.unreach).?), .loop => try genLoop(o, inst.castTag(.loop).?), + .condbr => try genCondBr(o, inst.castTag(.condbr).?), + .br => try genBr(o, inst.castTag(.br).?), + .brvoid => try genBrVoid(o, inst.castTag(.brvoid).?.block), else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), }; switch (result_value) { @@ -579,7 +584,31 @@ fn genDbgStmt(o: *Object, inst: *Inst.NoOp) !CValue { } fn genBlock(o: *Object, inst: *Inst.Block) !CValue { - return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement blocks", .{}); + const block_id: usize = o.next_block_index; + o.next_block_index += 1; + // abuse codegen.msv to store the block's id + inst.codegen.mcv.a = block_id; + try genBody(o, inst.body); + try o.indent_writer.insertNewline(); + // label must be followed by an expression, add an empty one. + try o.writer().print("zig_block_{d}:;\n", .{block_id}); + + // blocks in C cannot result in values + // TODO we need some other way to pass the result of the block + return CValue.none; +} + +fn genBr(o: *Object, inst: *Inst.Br) !CValue { + if (inst.operand.ty.tag() != .void) { + return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement block return values", .{}); + } + + return genBrVoid(o, inst.block); +} + +fn genBrVoid(o: *Object, block: *Inst.Block) !CValue { + try o.writer().print("goto zig_block_{d};\n", .{block.codegen.mcv.a}); + return CValue.none; } fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue { @@ -622,12 +651,27 @@ fn genUnreach(o: *Object, inst: *Inst.NoOp) !CValue { } fn genLoop(o: *Object, inst: *Inst.Loop) !CValue { - try o.writer().writeAll("while (true)"); + try o.writer().writeAll("while (true) "); try genBody(o, inst.body); try o.indent_writer.insertNewline(); return CValue.none; } +fn genCondBr(o: *Object, inst: *Inst.CondBr) !CValue { + const cond = try o.resolveInst(inst.condition); + const writer = o.writer(); + + try writer.writeAll("if ("); + try o.writeCValue(writer, cond); + try writer.writeAll(") "); + try genBody(o, inst.then_body); + try writer.writeAll(" else "); + try genBody(o, inst.else_body); + try o.indent_writer.insertNewline(); + + return CValue.none; +} + fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { if (as.base.isUnused() and !as.is_volatile) return CValue.none; diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 6d4e2062bf..c953d6077e 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -133,6 +133,24 @@ pub fn addCases(ctx: *TestContext) !void { \\} \\ , ""); + + // Simple while loop + case.addCompareOutput( + \\export fn main() c_int { + \\ var a: c_int = 0; + \\ while (a < 5) : (a+=1) {} + \\ exit(a - 5); + \\} + \\ + \\fn exit(code: usize) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ ); + \\ unreachable; + \\} + , ""); } { -- cgit v1.2.3 From 258f3ec5ecf8d2a165382d5837bed0dac2e0375b Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 27 Jan 2021 11:05:22 +0200 Subject: stage2 cbe: block results --- src/codegen/c.zig | 38 +++++++++++++++++++++++++++----------- test/stage2/cbe.zig | 21 +++++++++++++++++++++ 2 files changed, 48 insertions(+), 11 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/c.zig b/src/codegen/c.zig index bf6a5aac1f..e33f812f0b 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -325,7 +325,7 @@ pub fn genDecl(o: *Object) !void { const func: *Module.Fn = func_payload.data; try o.indent_writer.insertNewline(); try o.dg.renderFunctionSignature(o.writer(), is_global); - + try o.writer().writeByte(' '); try genBody(o, func.body); @@ -586,28 +586,44 @@ fn genDbgStmt(o: *Object, inst: *Inst.NoOp) !CValue { fn genBlock(o: *Object, inst: *Inst.Block) !CValue { const block_id: usize = o.next_block_index; o.next_block_index += 1; - // abuse codegen.msv to store the block's id - inst.codegen.mcv.a = block_id; + const writer = o.writer(); + + // store the block id in relocs.capacity as it is not used for anything else in the C backend. + inst.codegen.relocs.capacity = block_id; + const result = if (inst.base.ty.tag() != .void and !inst.base.isUnused()) blk: { + // allocate a location for the result + const local = try o.allocLocal(inst.base.ty, .Mut); + try writer.writeAll(";\n"); + break :blk local; + } else + CValue{ .none = {} }; + + inst.codegen.mcv = @bitCast(@import("../codegen.zig").AnyMCValue, result); try genBody(o, inst.body); try o.indent_writer.insertNewline(); // label must be followed by an expression, add an empty one. - try o.writer().print("zig_block_{d}:;\n", .{block_id}); - - // blocks in C cannot result in values - // TODO we need some other way to pass the result of the block - return CValue.none; + try writer.print("zig_block_{d}:;\n", .{block_id}); + return result; } fn genBr(o: *Object, inst: *Inst.Br) !CValue { - if (inst.operand.ty.tag() != .void) { - return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement block return values", .{}); + const result = @bitCast(CValue, inst.block.codegen.mcv); + const writer = o.writer(); + + // If result is .none then the value of the block is unused. + if (inst.operand.ty.tag() != .void and result != .none) { + const operand = try o.resolveInst(inst.operand); + try o.writeCValue(writer, result); + try writer.writeAll(" = "); + try o.writeCValue(writer, operand); + try writer.writeAll(";\n"); } return genBrVoid(o, inst.block); } fn genBrVoid(o: *Object, block: *Inst.Block) !CValue { - try o.writer().print("goto zig_block_{d};\n", .{block.codegen.mcv.a}); + try o.writer().print("goto zig_block_{d};\n", .{block.codegen.relocs.capacity}); return CValue.none; } diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index c953d6077e..8a264f5ca6 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -151,6 +151,27 @@ pub fn addCases(ctx: *TestContext) !void { \\ unreachable; \\} , ""); + + // If expression + case.addCompareOutput( + \\export fn main() c_int { + \\ var cond: c_int = 0; + \\ var a: c_int = @as(c_int, if (cond == 0) + \\ 2 + \\ else + \\ 3) + 9; + \\ exit(a - 11); + \\} + \\ + \\fn exit(code: usize) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ ); + \\ unreachable; + \\} + , ""); } { -- cgit v1.2.3 From 106520329e5adc6cf5ef83595da6c9d5dd3c4b35 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 27 Jan 2021 11:40:34 +0200 Subject: stage2 cbe: implement switchbr --- src/Module.zig | 4 +-- src/codegen/c.zig | 35 ++++++++++++++++++++++++ src/ir.zig | 4 +-- test/stage2/cbe.zig | 78 ++++++++++++++++++++++++++--------------------------- 4 files changed, 78 insertions(+), 43 deletions(-) (limited to 'src/codegen') diff --git a/src/Module.zig b/src/Module.zig index b495afb336..46c3d513f1 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2215,7 +2215,7 @@ pub fn addSwitchBr( self: *Module, block: *Scope.Block, src: usize, - target_ptr: *Inst, + target: *Inst, cases: []Inst.SwitchBr.Case, else_body: ir.Body, ) !*Inst { @@ -2226,7 +2226,7 @@ pub fn addSwitchBr( .ty = Type.initTag(.noreturn), .src = src, }, - .target_ptr = target_ptr, + .target = target, .cases = cases, .else_body = else_body, }; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index e33f812f0b..7fcbe44205 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -129,6 +129,9 @@ pub const DeclGen = struct { t: Type, val: Value, ) error{ OutOfMemory, AnalysisFail }!void { + if (val.isUndef()) { + return dg.fail(dg.decl.src(), "TODO: C backend: properly handle undefined in all cases (with debug safety?)", .{}); + } switch (t.zigTypeTag()) { .Int => { if (t.isSignedInt()) @@ -196,6 +199,7 @@ pub const DeclGen = struct { }, } }, + .Bool => return writer.print("{}", .{val.toBool()}), else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement value {s}", .{ @tagName(e), }), @@ -409,6 +413,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi .condbr => try genCondBr(o, inst.castTag(.condbr).?), .br => try genBr(o, inst.castTag(.br).?), .brvoid => try genBrVoid(o, inst.castTag(.brvoid).?.block), + .switchbr => try genSwitchBr(o, inst.castTag(.switchbr).?), + // booland and boolor are non-short-circuit operations + .booland => try genBinOp(o, inst.castTag(.booland).?, " & "), + .boolor => try genBinOp(o, inst.castTag(.boolor).?, " | "), else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), }; switch (result_value) { @@ -688,6 +696,33 @@ fn genCondBr(o: *Object, inst: *Inst.CondBr) !CValue { return CValue.none; } +fn genSwitchBr(o: *Object, inst: *Inst.SwitchBr) !CValue { + const target = try o.resolveInst(inst.target); + const writer = o.writer(); + + try writer.writeAll("switch ("); + try o.writeCValue(writer, target); + try writer.writeAll(") {\n"); + o.indent_writer.pushIndent(); + + for (inst.cases) |case| { + try writer.writeAll("case "); + try o.dg.renderValue(writer, inst.target.ty, case.item); + try writer.writeAll(": "); + // the case body must be noreturn so we don't need to insert a break + try genBody(o, case.body); + try o.indent_writer.insertNewline(); + } + + try writer.writeAll("default: "); + try genBody(o, inst.else_body); + try o.indent_writer.insertNewline(); + + o.indent_writer.popIndent(); + try writer.writeAll("}\n"); + return CValue.none; +} + fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { if (as.base.isUnused() and !as.is_volatile) return CValue.none; diff --git a/src/ir.zig b/src/ir.zig index 408efc3bba..0e83dbfd56 100644 --- a/src/ir.zig +++ b/src/ir.zig @@ -521,7 +521,7 @@ pub const Inst = struct { pub const base_tag = Tag.switchbr; base: Inst, - target_ptr: *Inst, + target: *Inst, cases: []Case, /// Set of instructions whose lifetimes end at the start of one of the cases. /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... ]. @@ -544,7 +544,7 @@ pub const Inst = struct { var i = index; if (i < 1) - return self.target_ptr; + return self.target; i -= 1; return null; diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 8a264f5ca6..aacb2b7077 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -133,45 +133,6 @@ pub fn addCases(ctx: *TestContext) !void { \\} \\ , ""); - - // Simple while loop - case.addCompareOutput( - \\export fn main() c_int { - \\ var a: c_int = 0; - \\ while (a < 5) : (a+=1) {} - \\ exit(a - 5); - \\} - \\ - \\fn exit(code: usize) noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (code) - \\ ); - \\ unreachable; - \\} - , ""); - - // If expression - case.addCompareOutput( - \\export fn main() c_int { - \\ var cond: c_int = 0; - \\ var a: c_int = @as(c_int, if (cond == 0) - \\ 2 - \\ else - \\ 3) + 9; - \\ exit(a - 11); - \\} - \\ - \\fn exit(code: usize) noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (code) - \\ ); - \\ unreachable; - \\} - , ""); } { @@ -224,6 +185,45 @@ pub fn addCases(ctx: *TestContext) !void { \\} , ""); } + { + var case = ctx.exeFromCompiledC("control flow", .{}); + + // Simple while loop + case.addCompareOutput( + \\export fn main() c_int { + \\ var a: c_int = 0; + \\ while (a < 5) : (a+=1) {} + \\ return a - 5; + \\} + , ""); + + // If expression + case.addCompareOutput( + \\export fn main() c_int { + \\ var cond: c_int = 0; + \\ var a: c_int = @as(c_int, if (cond == 0) + \\ 2 + \\ else + \\ 3) + 9; + \\ return a - 11; + \\} + , ""); + + // Switch expression + case.addCompareOutput( + \\export fn main() c_int { + \\ var cond: c_int = 0; + \\ var a: c_int = switch (cond) { + \\ 1 => 1, + \\ 2 => 2, + \\ 99...300, 12 => 3, + \\ 0 => 4, + \\ else => 5, + \\ }; + \\ return a - 4; + \\} + , ""); + } ctx.c("empty start function", linux_x64, \\export fn _start() noreturn { \\ unreachable; -- cgit v1.2.3 From 3ec5c9a3bcae09c01cbe4f0505e6ab03834bbb98 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 27 Jan 2021 12:22:38 +0200 Subject: stage2 cbe: implement not and some bitwise ops --- src/codegen/c.zig | 24 ++++++++++++++++++++++-- test/stage2/cbe.zig | 7 +++++++ 2 files changed, 29 insertions(+), 2 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 7fcbe44205..39fa80ea3d 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -293,6 +293,7 @@ pub const DeclGen = struct { try dg.renderType(w, t.elemType()); try w.writeAll(" *"); }, + .Null, .Undefined => unreachable, // must be const or comptime else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{ @tagName(e), }), @@ -387,6 +388,7 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi for (body.instructions) |inst| { const result_value = switch (inst.tag) { + .constant => unreachable, // excluded from function bodies .add => try genBinOp(o, inst.castTag(.add).?, " + "), .alloc => try genAlloc(o, inst.castTag(.alloc).?), .arg => genArg(o), @@ -415,8 +417,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi .brvoid => try genBrVoid(o, inst.castTag(.brvoid).?.block), .switchbr => try genSwitchBr(o, inst.castTag(.switchbr).?), // booland and boolor are non-short-circuit operations - .booland => try genBinOp(o, inst.castTag(.booland).?, " & "), - .boolor => try genBinOp(o, inst.castTag(.boolor).?, " | "), + .booland, .bitand => try genBinOp(o, inst.castTag(.booland).?, " & "), + .boolor, .bitor => try genBinOp(o, inst.castTag(.boolor).?, " | "), + .xor => try genBinOp(o, inst.castTag(.xor).?, " ^ "), + .not => try genUnOp(o, inst.castTag(.not).?, "!"), else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), }; switch (result_value) { @@ -541,6 +545,22 @@ fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue { return local; } +fn genUnOp(o: *Object, inst: *Inst.UnOp, operator: []const u8) !CValue { + if (inst.base.isUnused()) + return CValue.none; + + const operand = try o.resolveInst(inst.operand); + + const writer = o.writer(); + const local = try o.allocLocal(inst.base.ty, .Const); + + try writer.print(" = {s}", .{operator}); + try o.writeCValue(writer, operand); + try writer.writeAll(";\n"); + + return local; +} + fn genCall(o: *Object, inst: *Inst.Call) !CValue { if (inst.func.castTag(.constant)) |func_inst| { const fn_decl = if (func_inst.val.castTag(.extern_fn)) |extern_fn| diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index aacb2b7077..0eb2cf68b4 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -196,6 +196,13 @@ pub fn addCases(ctx: *TestContext) !void { \\ return a - 5; \\} , ""); + case.addCompareOutput( + \\export fn main() c_int { + \\ var a = true; + \\ while (!a) {} + \\ return 0; + \\} + , ""); // If expression case.addCompareOutput( -- cgit v1.2.3 From 75acfcf0eaa306b3a8872e50cb735e1d5eb18c52 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 1 Feb 2021 15:45:11 +0200 Subject: stage2: reimplement switch --- src/astgen.zig | 313 +++++++++++++++++++++++++++++++++++++++++++++++++++++- src/codegen/c.zig | 10 +- src/zir.zig | 60 +++++++++++ src/zir_sema.zig | 228 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 606 insertions(+), 5 deletions(-) (limited to 'src/codegen') diff --git a/src/astgen.zig b/src/astgen.zig index dfc5f06ddc..ece16d70da 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -309,7 +309,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?), .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?), .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?), - .Switch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Switch", .{}), + .Switch => return switchExpr(mod, scope, rl, node.castTag(.Switch).?), .ContainerDecl => return containerDecl(mod, scope, rl, node.castTag(.ContainerDecl).?), .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), @@ -2246,6 +2246,317 @@ fn forExpr( ); } +fn switchCaseUsesRef(node: *ast.Node.Switch) bool { + for (node.cases()) |uncasted_case| { + const case = uncasted_case.castTag(.SwitchCase).?; + const uncasted_payload = case.payload orelse continue; + const payload = uncasted_payload.castTag(.PointerPayload).?; + if (payload.ptr_token) |_| return true; + } + return false; +} + +fn getRangeNode(node: *ast.Node) ?*ast.Node.SimpleInfixOp { + var cur = node; + while (true) { + switch (cur.tag) { + .Range => return @fieldParentPtr(ast.Node.SimpleInfixOp, "base", cur), + .GroupedExpression => cur = @fieldParentPtr(ast.Node.GroupedExpression, "base", cur).expr, + else => return null, + } + } +} + +fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node.Switch) InnerError!*zir.Inst { + const tree = scope.tree(); + const switch_src = tree.token_locs[switch_node.switch_token].start; + const use_ref = switchCaseUsesRef(switch_node); + + var block_scope: Scope.GenZIR = .{ + .parent = scope, + .decl = scope.ownerDecl().?, + .arena = scope.arena(), + .force_comptime = scope.isComptime(), + .instructions = .{}, + }; + setBlockResultLoc(&block_scope, rl); + defer block_scope.instructions.deinit(mod.gpa); + + var items = std.ArrayList(*zir.Inst).init(mod.gpa); + defer items.deinit(); + + // first we gather all the switch items and check else/'_' prongs + var else_src: ?usize = null; + var underscore_src: ?usize = null; + var first_range: ?*zir.Inst = null; + var simple_case_count: usize = 0; + for (switch_node.cases()) |uncasted_case| { + const case = uncasted_case.castTag(.SwitchCase).?; + const case_src = tree.token_locs[case.firstToken()].start; + assert(case.items_len != 0); + + // Check for else/_ prong, those are handled last. + if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) { + if (else_src) |src| { + const msg = msg: { + const msg = try mod.errMsg( + scope, + case_src, + "multiple else prongs in switch expression", + .{}, + ); + errdefer msg.destroy(mod.gpa); + try mod.errNote(scope, src, msg, "previous else prong is here", .{}); + break :msg msg; + }; + return mod.failWithOwnedErrorMsg(scope, msg); + } + else_src = case_src; + continue; + } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and + mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) + { + if (underscore_src) |src| { + const msg = msg: { + const msg = try mod.errMsg( + scope, + case_src, + "multiple '_' prongs in switch expression", + .{}, + ); + errdefer msg.destroy(mod.gpa); + try mod.errNote(scope, src, msg, "previous '_' prong is here", .{}); + break :msg msg; + }; + return mod.failWithOwnedErrorMsg(scope, msg); + } + underscore_src = case_src; + continue; + } + + if (else_src) |some_else| { + if (underscore_src) |some_underscore| { + const msg = msg: { + const msg = try mod.errMsg( + scope, + switch_src, + "else and '_' prong in switch expression", + .{}, + ); + errdefer msg.destroy(mod.gpa); + try mod.errNote(scope, some_else, msg, "else prong is here", .{}); + try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{}); + break :msg msg; + }; + return mod.failWithOwnedErrorMsg(scope, msg); + } + } + + if (case.items_len == 1 and getRangeNode(case.items()[0]) == null) simple_case_count += 1; + + // generate all the switch items as comptime expressions + for (case.items()) |item| { + if (getRangeNode(item)) |range| { + const start = try comptimeExpr(mod, &block_scope.base, .none, range.lhs); + const end = try comptimeExpr(mod, &block_scope.base, .none, range.rhs); + const range_src = tree.token_locs[range.op_token].start; + const range_inst = try addZIRBinOp(mod, &block_scope.base, range_src, .switch_range, start, end); + try items.append(range_inst); + } else { + const item_inst = try comptimeExpr(mod, &block_scope.base, .none, item); + try items.append(item_inst); + } + } + } + + var special_prong: zir.Inst.SwitchBr.SpecialProng = .none; + if (else_src != null) special_prong = .@"else"; + if (underscore_src != null) special_prong = .underscore; + var cases = try block_scope.arena.alloc(zir.Inst.SwitchBr.Case, simple_case_count); + + const target_ptr = if (use_ref) try expr(mod, &block_scope.base, .ref, switch_node.expr) else null; + const target = if (target_ptr) |some| + try addZIRUnOp(mod, &block_scope.base, some.src, .deref, some) + else + try expr(mod, &block_scope.base, .none, switch_node.expr); + const switch_inst = try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{ + .target = target, + .cases = cases, + .items = try block_scope.arena.dupe(*zir.Inst, items.items), + .else_body = undefined, // populated below + }, .{ + .range = first_range, + .special_prong = special_prong, + }); + + const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{ + .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), + }); + + var case_scope: Scope.GenZIR = .{ + .parent = scope, + .decl = block_scope.decl, + .arena = block_scope.arena, + .force_comptime = block_scope.force_comptime, + .instructions = .{}, + }; + defer case_scope.instructions.deinit(mod.gpa); + + var else_scope: Scope.GenZIR = .{ + .parent = scope, + .decl = case_scope.decl, + .arena = case_scope.arena, + .force_comptime = case_scope.force_comptime, + .instructions = .{}, + }; + defer else_scope.instructions.deinit(mod.gpa); + + // Now generate all but the special cases + var special_case: ?*ast.Node.SwitchCase = null; + var items_index: usize = 0; + var case_index: usize = 0; + for (switch_node.cases()) |uncasted_case| { + const case = uncasted_case.castTag(.SwitchCase).?; + const case_src = tree.token_locs[case.firstToken()].start; + // reset without freeing to reduce allocations. + case_scope.instructions.items.len = 0; + + // Check for else/_ prong, those are handled last. + if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) { + special_case = case; + continue; + } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and + mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) + { + special_case = case; + continue; + } + + // If this is a simple one item prong then it is handled by the switchbr. + if (case.items_len == 1 and getRangeNode(case.items()[0]) == null) { + const item = items.items[items_index]; + items_index += 1; + try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target, target_ptr); + + cases[case_index] = .{ + .item = item, + .body = .{ .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items) }, + }; + case_index += 1; + continue; + } + + // TODO if the case has few items and no ranges it might be better + // to just handle them as switch prongs. + + // Check if the target matches any of the items. + // 1, 2, 3..6 will result in + // target == 1 or target == 2 or (target >= 3 and target <= 6) + var any_ok: ?*zir.Inst = null; + for (case.items()) |item| { + if (getRangeNode(item)) |range| { + const range_src = tree.token_locs[range.op_token].start; + const range_inst = items.items[items_index].castTag(.switch_range).?; + items_index += 1; + + // target >= start and target <= end + const range_start_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_gte, target, range_inst.positionals.lhs); + const range_end_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_lte, target, range_inst.positionals.rhs); + const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_and, range_start_ok, range_end_ok); + + if (any_ok) |some| { + any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_or, some, range_ok); + } else { + any_ok = range_ok; + } + continue; + } + + const item_inst = items.items[items_index]; + items_index += 1; + const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst); + + if (any_ok) |some| { + any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .bool_or, some, cpm_ok); + } else { + any_ok = cpm_ok; + } + } + + const condbr = try addZIRInstSpecial(mod, &case_scope.base, case_src, zir.Inst.CondBr, .{ + .condition = any_ok.?, + .then_body = undefined, // populated below + .else_body = undefined, // populated below + }, .{}); + const cond_block = try addZIRInstBlock(mod, &else_scope.base, case_src, .block, .{ + .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items), + }); + + // reset cond_scope for then_body + case_scope.instructions.items.len = 0; + try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target, target_ptr); + condbr.positionals.then_body = .{ + .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items), + }; + + // reset cond_scope for else_body + case_scope.instructions.items.len = 0; + _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.BreakVoid, .{ + .block = cond_block, + }, .{}); + condbr.positionals.else_body = .{ + .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items), + }; + } + + // Finally generate else block or a break. + if (special_case) |case| { + try switchCaseExpr(mod, &else_scope.base, block_scope.break_result_loc, block, case, target, target_ptr); + } else { + // Not handling all possible cases is a compile error. + _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe); + } + switch_inst.castTag(.switchbr).?.positionals.else_body = .{ + .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items), + }; + + return &block.base; +} + +fn switchCaseExpr( + mod: *Module, + scope: *Scope, + rl: ResultLoc, + block: *zir.Inst.Block, + case: *ast.Node.SwitchCase, + target: *zir.Inst, + target_ptr: ?*zir.Inst, +) !void { + const tree = scope.tree(); + const case_src = tree.token_locs[case.firstToken()].start; + const sub_scope = blk: { + const uncasted_payload = case.payload orelse break :blk scope; + const payload = uncasted_payload.castTag(.PointerPayload).?; + const is_ptr = payload.ptr_token != null; + const value_name = tree.tokenSlice(payload.value_symbol.firstToken()); + if (mem.eql(u8, value_name, "_")) { + if (is_ptr) { + return mod.failTok(scope, payload.ptr_token.?, "pointer modifier invalid on discard", .{}); + } + break :blk scope; + } + return mod.failNode(scope, payload.value_symbol, "TODO implement switch value payload", .{}); + }; + + const case_body = try expr(mod, sub_scope, rl, case.expr); + if (!case_body.tag.isNoReturn()) { + _ = try addZIRInst(mod, sub_scope, case_src, zir.Inst.Break, .{ + .block = block, + .operand = case_body, + }, .{}); + } +} + fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[cfe.ltoken].start; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 39fa80ea3d..cb3271a57f 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -414,11 +414,13 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi .loop => try genLoop(o, inst.castTag(.loop).?), .condbr => try genCondBr(o, inst.castTag(.condbr).?), .br => try genBr(o, inst.castTag(.br).?), - .brvoid => try genBrVoid(o, inst.castTag(.brvoid).?.block), + .br_void => try genBrVoid(o, inst.castTag(.br_void).?.block), .switchbr => try genSwitchBr(o, inst.castTag(.switchbr).?), - // booland and boolor are non-short-circuit operations - .booland, .bitand => try genBinOp(o, inst.castTag(.booland).?, " & "), - .boolor, .bitor => try genBinOp(o, inst.castTag(.boolor).?, " | "), + // bool_and and bool_or are non-short-circuit operations + .bool_and => try genBinOp(o, inst.castTag(.bool_and).?, " & "), + .bool_or => try genBinOp(o, inst.castTag(.bool_or).?, " | "), + .bit_and => try genBinOp(o, inst.castTag(.bit_and).?, " & "), + .bit_or => try genBinOp(o, inst.castTag(.bit_or).?, " | "), .xor => try genBinOp(o, inst.castTag(.xor).?, " ^ "), .not => try genUnOp(o, inst.castTag(.not).?, "!"), else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), diff --git a/src/zir.zig b/src/zir.zig index 2559fcdc8e..30bfeead9b 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -338,6 +338,12 @@ pub const Inst = struct { enum_type, /// Does nothing; returns a void value. void_value, + /// A switch expression. + switchbr, + /// A range in a switch case, `lhs...rhs`. + /// Only checks that `lhs >= rhs` if they are ints, everything else is + /// validated by the .switch instruction. + switch_range, pub fn Type(tag: Tag) type { return switch (tag) { @@ -435,6 +441,7 @@ pub const Inst = struct { .error_union_type, .merge_error_sets, .slice_start, + .switch_range, => BinOp, .block, @@ -478,6 +485,7 @@ pub const Inst = struct { .enum_type => EnumType, .union_type => UnionType, .struct_type => StructType, + .switchbr => SwitchBr, }; } @@ -605,6 +613,8 @@ pub const Inst = struct { .union_type, .struct_type, .void_value, + .switch_range, + .switchbr, => false, .@"break", @@ -1171,6 +1181,36 @@ pub const Inst = struct { none, }; }; + + pub const SwitchBr = struct { + pub const base_tag = Tag.switchbr; + base: Inst, + + positionals: struct { + target: *Inst, + /// List of all individual items and ranges + items: []*Inst, + cases: []Case, + else_body: Body, + }, + kw_args: struct { + /// Pointer to first range if such exists. + range: ?*Inst = null, + special_prong: SpecialProng = .none, + }, + + // Not anonymous due to stage1 limitations + pub const SpecialProng = enum { + none, + @"else", + underscore, + }; + + pub const Case = struct { + item: *Inst, + body: Body, + }; + }; }; pub const ErrorMsg = struct { @@ -1431,6 +1471,26 @@ const Writer = struct { } try stream.writeByte(']'); }, + []Inst.SwitchBr.Case => { + if (param.len == 0) { + return stream.writeAll("{}"); + } + try stream.writeAll("{\n"); + for (param) |*case, i| { + if (i != 0) { + try stream.writeAll(",\n"); + } + try stream.writeByteNTimes(' ', self.indent); + self.indent += 2; + try self.writeParamToStream(stream, &case.item); + try stream.writeAll(" => "); + try self.writeParamToStream(stream, &case.body); + self.indent -= 2; + } + try stream.writeByte('\n'); + try stream.writeByteNTimes(' ', self.indent - 2); + try stream.writeByte('}'); + }, else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)), } } diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 301b95ad97..f373d7174d 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -154,6 +154,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .bool_and => return zirBoolOp(mod, scope, old_inst.castTag(.bool_and).?), .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?), .void_value => return mod.constVoid(scope, old_inst.src), + .switchbr => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr).?), + .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?), .container_field_named, .container_field_typed, @@ -1535,6 +1537,232 @@ fn zirSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError! return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null); } +fn zirSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); + const start = try resolveInst(mod, scope, inst.positionals.lhs); + const end = try resolveInst(mod, scope, inst.positionals.rhs); + + switch (start.ty.zigTypeTag()) { + .Int, .ComptimeInt => {}, + else => return mod.constVoid(scope, inst.base.src), + } + switch (end.ty.zigTypeTag()) { + .Int, .ComptimeInt => {}, + else => return mod.constVoid(scope, inst.base.src), + } + // .switch_range must be inside a comptime scope + const start_val = start.value().?; + const end_val = end.value().?; + if (start_val.compare(.gte, end_val)) { + return mod.fail(scope, inst.base.src, "range start value must be smaller than the end value", .{}); + } + return mod.constVoid(scope, inst.base.src); +} + +fn zirSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); + const target = try resolveInst(mod, scope, inst.positionals.target); + try validateSwitch(mod, scope, target, inst); + + if (try mod.resolveDefinedValue(scope, target)) |target_val| { + for (inst.positionals.cases) |case| { + const resolved = try resolveInst(mod, scope, case.item); + const casted = try mod.coerce(scope, target.ty, resolved); + const item = try mod.resolveConstValue(scope, casted); + + if (target_val.eql(item)) { + try analyzeBody(mod, scope.cast(Scope.Block).?, case.body); + return mod.constNoReturn(scope, inst.base.src); + } + } + try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body); + return mod.constNoReturn(scope, inst.base.src); + } + + if (inst.positionals.cases.len == 0) { + // no cases just analyze else_branch + try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body); + return mod.constNoReturn(scope, inst.base.src); + } + + const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src); + const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, inst.positionals.cases.len); + + var case_block: Scope.Block = .{ + .parent = parent_block, + .inst_table = parent_block.inst_table, + .func = parent_block.func, + .owner_decl = parent_block.owner_decl, + .src_decl = parent_block.src_decl, + .instructions = .{}, + .arena = parent_block.arena, + .inlining = parent_block.inlining, + .is_comptime = parent_block.is_comptime, + .branch_quota = parent_block.branch_quota, + }; + defer case_block.instructions.deinit(mod.gpa); + + for (inst.positionals.cases) |case, i| { + // Reset without freeing. + case_block.instructions.items.len = 0; + + const resolved = try resolveInst(mod, scope, case.item); + const casted = try mod.coerce(scope, target.ty, resolved); + const item = try mod.resolveConstValue(scope, casted); + + try analyzeBody(mod, &case_block, case.body); + + cases[i] = .{ + .item = item, + .body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items) }, + }; + } + + case_block.instructions.items.len = 0; + try analyzeBody(mod, &case_block, inst.positionals.else_body); + + const else_body: ir.Body = .{ + .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items), + }; + + return mod.addSwitchBr(parent_block, inst.base.src, target, cases, else_body); +} + +fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void { + // validate usage of '_' prongs + if (inst.kw_args.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) { + return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{}); + // TODO notes "'_' prong here" inst.positionals.cases[last].src + } + + // check that target type supports ranges + if (inst.kw_args.range) |range_inst| { + switch (target.ty.zigTypeTag()) { + .Int, .ComptimeInt => {}, + else => { + return mod.fail(scope, target.src, "ranges not allowed when switching on type {}", .{target.ty}); + // TODO notes "range used here" range_inst.src + }, + } + } + + // validate for duplicate items/missing else prong + switch (target.ty.zigTypeTag()) { + .Enum => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Enum", .{}), + .ErrorSet => return mod.fail(scope, inst.base.src, "TODO validateSwitch .ErrorSet", .{}), + .Union => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Union", .{}), + .Int, .ComptimeInt => { + var range_set = @import("RangeSet.zig").init(mod.gpa); + defer range_set.deinit(); + + for (inst.positionals.items) |item| { + const maybe_src = if (item.castTag(.switch_range)) |range| blk: { + const start_resolved = try resolveInst(mod, scope, range.positionals.lhs); + const start_casted = try mod.coerce(scope, target.ty, start_resolved); + const end_resolved = try resolveInst(mod, scope, range.positionals.rhs); + const end_casted = try mod.coerce(scope, target.ty, end_resolved); + + break :blk try range_set.add( + try mod.resolveConstValue(scope, start_casted), + try mod.resolveConstValue(scope, end_casted), + item.src, + ); + } else blk: { + const resolved = try resolveInst(mod, scope, item); + const casted = try mod.coerce(scope, target.ty, resolved); + const value = try mod.resolveConstValue(scope, casted); + break :blk try range_set.add(value, value, item.src); + }; + + if (maybe_src) |previous_src| { + return mod.fail(scope, item.src, "duplicate switch value", .{}); + // TODO notes "previous value is here" previous_src + } + } + + if (target.ty.zigTypeTag() == .Int) { + var arena = std.heap.ArenaAllocator.init(mod.gpa); + defer arena.deinit(); + + const start = try target.ty.minInt(&arena, mod.getTarget()); + const end = try target.ty.maxInt(&arena, mod.getTarget()); + if (try range_set.spans(start, end)) { + if (inst.kw_args.special_prong == .@"else") { + return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); + } + return; + } + } + + if (inst.kw_args.special_prong != .@"else") { + return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{}); + } + }, + .Bool => { + var true_count: u8 = 0; + var false_count: u8 = 0; + for (inst.positionals.items) |item| { + const resolved = try resolveInst(mod, scope, item); + const casted = try mod.coerce(scope, Type.initTag(.bool), resolved); + if ((try mod.resolveConstValue(scope, casted)).toBool()) { + true_count += 1; + } else { + false_count += 1; + } + + if (true_count + false_count > 2) { + return mod.fail(scope, item.src, "duplicate switch value", .{}); + } + } + if ((true_count + false_count < 2) and inst.kw_args.special_prong != .@"else") { + return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{}); + } + if ((true_count + false_count == 2) and inst.kw_args.special_prong == .@"else") { + return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); + } + }, + .EnumLiteral, .Void, .Fn, .Pointer, .Type => { + if (inst.kw_args.special_prong != .@"else") { + return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty}); + } + + var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(mod.gpa); + defer seen_values.deinit(); + + for (inst.positionals.items) |item| { + const resolved = try resolveInst(mod, scope, item); + const casted = try mod.coerce(scope, target.ty, resolved); + const val = try mod.resolveConstValue(scope, casted); + + if (try seen_values.fetchPut(val, item.src)) |prev| { + return mod.fail(scope, item.src, "duplicate switch value", .{}); + // TODO notes "previous value here" prev.value + } + } + }, + + .ErrorUnion, + .NoReturn, + .Array, + .Struct, + .Undefined, + .Null, + .Optional, + .BoundFn, + .Opaque, + .Vector, + .Frame, + .AnyFrame, + .ComptimeFloat, + .Float, + => { + return mod.fail(scope, target.src, "invalid switch target type '{}'", .{target.ty}); + }, + } +} + fn zirImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); -- cgit v1.2.3 From c2beaba85a87b5985fe9f1676ad2bc4888dd6c1a Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Mon, 8 Feb 2021 22:29:41 +0100 Subject: stage2 ARM: fix callee_preserved_regs Previously, the registers included r0, r1, r2, r3 which are not included in the callee saved registers according to the Procedure Call Standard for the ARM Architecture. --- src/codegen/arm.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/codegen') diff --git a/src/codegen/arm.zig b/src/codegen/arm.zig index 94f1ae951d..d538d28c50 100644 --- a/src/codegen/arm.zig +++ b/src/codegen/arm.zig @@ -186,7 +186,7 @@ pub const Psr = enum { spsr, }; -pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 }; +pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 }; pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; -- cgit v1.2.3