Home | History | Annotate | Download | only in val
      1 // Copyright (c) 2015-2016 The Khronos Group Inc.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 // Source code for logical layout validation as described in section 2.4
     16 
     17 #include "source/val/validate.h"
     18 
     19 #include <cassert>
     20 
     21 #include "source/diagnostic.h"
     22 #include "source/opcode.h"
     23 #include "source/operand.h"
     24 #include "source/val/function.h"
     25 #include "source/val/instruction.h"
     26 #include "source/val/validation_state.h"
     27 
     28 namespace spvtools {
     29 namespace val {
     30 namespace {
     31 
     32 // Module scoped instructions are processed by determining if the opcode
     33 // is part of the current layout section. If it is not then the next sections is
     34 // checked.
     35 spv_result_t ModuleScopedInstructions(ValidationState_t& _,
     36                                       const Instruction* inst, SpvOp opcode) {
     37   while (_.IsOpcodeInCurrentLayoutSection(opcode) == false) {
     38     _.ProgressToNextLayoutSectionOrder();
     39 
     40     switch (_.current_layout_section()) {
     41       case kLayoutMemoryModel:
     42         if (opcode != SpvOpMemoryModel) {
     43           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
     44                  << spvOpcodeString(opcode)
     45                  << " cannot appear before the memory model instruction";
     46         }
     47         break;
     48       case kLayoutFunctionDeclarations:
     49         // All module sections have been processed. Recursively call
     50         // ModuleLayoutPass to process the next section of the module
     51         return ModuleLayoutPass(_, inst);
     52       default:
     53         break;
     54     }
     55   }
     56   return SPV_SUCCESS;
     57 }
     58 
     59 // Function declaration validation is performed by making sure that the
     60 // FunctionParameter and FunctionEnd instructions only appear inside of
     61 // functions. It also ensures that the Function instruction does not appear
     62 // inside of another function. This stage ends when the first label is
     63 // encountered inside of a function.
     64 spv_result_t FunctionScopedInstructions(ValidationState_t& _,
     65                                         const Instruction* inst, SpvOp opcode) {
     66   if (_.IsOpcodeInCurrentLayoutSection(opcode)) {
     67     switch (opcode) {
     68       case SpvOpFunction: {
     69         if (_.in_function_body()) {
     70           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
     71                  << "Cannot declare a function in a function body";
     72         }
     73         auto control_mask = inst->GetOperandAs<SpvFunctionControlMask>(2);
     74         if (auto error =
     75                 _.RegisterFunction(inst->id(), inst->type_id(), control_mask,
     76                                    inst->GetOperandAs<uint32_t>(3)))
     77           return error;
     78         if (_.current_layout_section() == kLayoutFunctionDefinitions) {
     79           if (auto error = _.current_function().RegisterSetFunctionDeclType(
     80                   FunctionDecl::kFunctionDeclDefinition))
     81             return error;
     82         }
     83       } break;
     84 
     85       case SpvOpFunctionParameter:
     86         if (_.in_function_body() == false) {
     87           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
     88                  << "Function parameter instructions must be in a "
     89                     "function body";
     90         }
     91         if (_.current_function().block_count() != 0) {
     92           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
     93                  << "Function parameters must only appear immediately after "
     94                     "the function definition";
     95         }
     96         if (auto error = _.current_function().RegisterFunctionParameter(
     97                 inst->id(), inst->type_id()))
     98           return error;
     99         break;
    100 
    101       case SpvOpFunctionEnd:
    102         if (_.in_function_body() == false) {
    103           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
    104                  << "Function end instructions must be in a function body";
    105         }
    106         if (_.in_block()) {
    107           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
    108                  << "Function end cannot be called in blocks";
    109         }
    110         if (_.current_function().block_count() == 0 &&
    111             _.current_layout_section() == kLayoutFunctionDefinitions) {
    112           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
    113                  << "Function declarations must appear before "
    114                     "function definitions.";
    115         }
    116         if (_.current_layout_section() == kLayoutFunctionDeclarations) {
    117           if (auto error = _.current_function().RegisterSetFunctionDeclType(
    118                   FunctionDecl::kFunctionDeclDeclaration))
    119             return error;
    120         }
    121         if (auto error = _.RegisterFunctionEnd()) return error;
    122         break;
    123 
    124       case SpvOpLine:
    125       case SpvOpNoLine:
    126         break;
    127       case SpvOpLabel:
    128         // If the label is encountered then the current function is a
    129         // definition so set the function to a declaration and update the
    130         // module section
    131         if (_.in_function_body() == false) {
    132           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
    133                  << "Label instructions must be in a function body";
    134         }
    135         if (_.in_block()) {
    136           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
    137                  << "A block must end with a branch instruction.";
    138         }
    139         if (_.current_layout_section() == kLayoutFunctionDeclarations) {
    140           _.ProgressToNextLayoutSectionOrder();
    141           if (auto error = _.current_function().RegisterSetFunctionDeclType(
    142                   FunctionDecl::kFunctionDeclDefinition))
    143             return error;
    144         }
    145         break;
    146 
    147       default:
    148         if (_.current_layout_section() == kLayoutFunctionDeclarations &&
    149             _.in_function_body()) {
    150           return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
    151                  << "A function must begin with a label";
    152         } else {
    153           if (_.in_block() == false) {
    154             return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
    155                    << spvOpcodeString(opcode) << " must appear in a block";
    156           }
    157         }
    158         break;
    159     }
    160   } else {
    161     return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
    162            << spvOpcodeString(opcode)
    163            << " cannot appear in a function declaration";
    164   }
    165   return SPV_SUCCESS;
    166 }
    167 
    168 }  // namespace
    169 
    170 // TODO(umar): Check linkage capabilities for function declarations
    171 // TODO(umar): Better error messages
    172 // NOTE: This function does not handle CFG related validation
    173 // Performs logical layout validation. See Section 2.4
    174 spv_result_t ModuleLayoutPass(ValidationState_t& _, const Instruction* inst) {
    175   const SpvOp opcode = inst->opcode();
    176 
    177   switch (_.current_layout_section()) {
    178     case kLayoutCapabilities:
    179     case kLayoutExtensions:
    180     case kLayoutExtInstImport:
    181     case kLayoutMemoryModel:
    182     case kLayoutEntryPoint:
    183     case kLayoutExecutionMode:
    184     case kLayoutDebug1:
    185     case kLayoutDebug2:
    186     case kLayoutDebug3:
    187     case kLayoutAnnotations:
    188     case kLayoutTypes:
    189       if (auto error = ModuleScopedInstructions(_, inst, opcode)) return error;
    190       break;
    191     case kLayoutFunctionDeclarations:
    192     case kLayoutFunctionDefinitions:
    193       if (auto error = FunctionScopedInstructions(_, inst, opcode)) {
    194         return error;
    195       }
    196       break;
    197   }
    198   return SPV_SUCCESS;
    199 }
    200 
    201 }  // namespace val
    202 }  // namespace spvtools
    203