Intelligent core security level software compiler formal verification method based on translation confirmation
Through the formal verification method of intelligent nuclear security-level software compiler based on translation confirmation, the Z3 constraint solver and hash encoding are used to solve the problem of inefficient verification in the existing technology, and fast and accurate compiler code verification is achieved. Improves the reliability and security of the system.
Patent Information
- Application Number
- CN202411329822.4
- Authority / Receiving Office
- CN · China
- Patent Type
- Applications(China)
- Current Assignee / Owner
- Filing Date
- 2024-09-24
- Publication Date
- 2024-12-27
AI Technical Summary
Existing software compiler verification methods are difficult to directly check the correctness between the compiler output code and the input code, and the formal proof requirements for the entire compiler are cumbersome, resulting in inefficient verification.
The formal verification method of intelligent nuclear security-level software compiler based on translation confirmation is adopted, and the IR file is analyzed using the Z3 constraint solver, and the integrity test is completed through the impact of the SMT solver timeout time, and an array of counting elements simulates multiple sets. Using hash encoding shows better performance in overall testing.
Directly checking the correctness of the compiler output code and input code improves verification speed, reduces the formal proof requirement for compiler implementation, significantly improves the training and inference speed, and ensures the reliability and security of the system.
Smart Images

Figure CN119201067A_ABST
Abstract
Description
Technical Field
[0001] The present invention relates to the field of computer software verification, and mainly to a formal verification method of an intelligent nuclear safety-level software compiler based on translation confirmation. Background Art
[0002] The nuclear safety system is a series of technical and management measures to ensure the safe operation of nuclear facilities (such as nuclear power plants, nuclear research reactors, etc.) during normal operation, emergency situations and potential accidents. Its main purpose is to prevent the occurrence of nuclear accidents, and to minimize their impact when they occur, to protect the safety of workers, the public and the environment. The digital control system is an automation system used for industrial process control. It is designed to improve the reliability, flexibility and operability of the system through distributed control and monitoring. It is the "central nervous system" of the nuclear power plant, which plays a vital role in ensuring the safe and economic operation of the nuclear power plant. It is one of the key technologies and equipment that constitute a large nuclear power plant. The design of such a system must not only meet specific functional requirements, but also non-functional requirements such as safety, reliability, and operating time. Among them, the safety of the system is the most important.
[0003] Among the existing verification methods, the translation confirmation method is a formal method for constructing a trusted compiler. It proves the correctness of the compiler by proving the semantic equivalence of the source code and the target code, avoiding the verification of the compiler itself. At the same time, it has good reusability. In recent years, it has been widely studied in the field of compiler verification and has achieved remarkable results. The translation confirmation technology has the following advantages:
[0004] 1) The translation confirmation method has a systematic and standardized process, and each step has clear goals and methods to ensure that each translation project can be carried out according to a unified high standard. The standardized process can be reused and applied to different translation projects to provide consistent quality assurance.
[0005] 2) Verification and inspection can be carried out at the early stage of translation, potential problems can be discovered and solved early, and the cost of later modification and rework can be reduced. Through multiple proofreading and multi-party review, problems at different levels can be comprehensively discovered and solved, improving the comprehensiveness and accuracy of the translation.
[0006] 3) Use computer-assisted translation tools and automated quality inspection tools to improve the efficiency of inspection and correction and reduce manual workload. Different confirmation steps can be carried out in parallel, shortening the overall project time and increasing the speed of project delivery.
[0007] The translation confirmation method effectively improves the accuracy, consistency and efficiency of translation, reduces risks and ensures high-quality translation results through its systematic, standardized, multi-level and automated process. This process is particularly suitable for projects with extremely high requirements for accuracy and consistency. Through confirmation, the reliability and security of the system can be greatly improved.
[0008] Z3 is an excellent SMT solver (also known as theorem prover) developed by a company. It can automatically check the satisfiability (SAT) of logical formulas, that is, determine whether a given logical formula has a variable assignment that makes the formula true. Z3 is a low-level tool, and it is best applied as a component to other tools that need to solve logical formulas. For ease of use, Z3 provides a lot of APIs, and these APIs support languages such as C, .NET, and OCaml. Of course, Z3 can also be executed through the command line. Z3 is built based on SMT theory and is used to determine the satisfiability of first-order logic. Since SMT introduces semantic interpretation, the necessary computing space is reduced, and the satisfiability problem is transformed into a decidability problem. In addition, Z3 shows good performance in integrity testing, so the present invention verifies the system framework with the help of Z3 and gives a conclusion on whether the requirements meet the expected system properties. Summary of the invention
[0009] The purpose of the present invention is to provide a formal verification method for an intelligent nuclear safety-level software compiler based on translation confirmation, which verifies the system framework with the help of a Z3 constraint solver, and obtains the influence of the SMT solver timeout time by analyzing the IR file pair; completes the integrity test, and defines an array of counting the number of elements to simulate a multiset, and obtains that hash coding is faster than multiset coding in the overall test, especially showing better performance when inputting large quantities. The present invention directly checks the correctness between the output code and the input code of the compiler, and does not need to formally prove the implementation of the entire compiler, which greatly improves the speed of training and reasoning.
[0010] The specific technical solution for achieving the purpose of the present invention is:
[0011] A formal verification method for an intelligent nuclear safety-level software compiler based on translation confirmation, the method comprising the following steps:
[0012] Step 1: Construct the semantic model of the intelligent nuclear safety-level software compiler, namely Synlong operational semantics
[0013] 1.1: SynLong Specification
[0014] SynLong's lexical and grammar are based on and extended from the synchronous data flow language Lustre; SynLong's lexical part defines keywords, identifiers, constants, operators and separators, prefix operators, whitespace symbols, and comments; SynLong's grammar specification defines programs and packages, types, global flows, user-defined operators, operator bodies, and expressions;
[0015] 1.2: Formal Semantics of State Machines
[0016] The state machine is a special type of automaton, which is represented by a six-tuple: A = (Q, ∑, δ, q0, F, V), where Q is the state set, ∑ is the input symbol, δ is the transition function, q0 is the initial state, F is the set of terminal states, and V is the variable defined in the automaton;
[0017] 1.3: Formal semantics of basic operators
[0018] definition These predicates, and based on the grammatical definition, write the semantics of the 'pre', 'fby', and '->' operators in the temporal operator;
[0019] 1.4: Formal semantics of higher-order operators
[0020] Synlong provides high-level primitives for modifying operator behavior: the input consists of a function operator and a variable-length empty argument list, and the output is another operator;
[0021] Step 2: Encoding Semantics in SMT
[0022] First, define the value, register file, and program state; then the register file assigns a value to each register. Depending on the value, when the value is used, an element in the set will be non-deterministically selected, and a new block will be obtained for each assignment; the final state is the register assignment, which contains the symbol noreturn, which means no return; the function ends with a call to a function that does not return;
[0023] Use the malloc function to let each stack variable and global variable obtain an independent memory block. Each memory block has a unique non-negative integer identifier bid to identify and reference the memory block. After the loop is unrolled, the maximum number of memory blocks that the program can access is statically calculated to limit the number of bits required to encode the bid. The pointer is defined as a tuple (bid, off). The pointer arithmetic instruction returns a new pointer by changing the offset. Use an SMT array from pointer to byte to encode the value of the memory block, that is, the content of the memory block is represented as a mapping from pointer to byte. There are three types of bytes: poison, pointer, and non-pointer. For non-pointer bytes, use an 8-bit bit vector to store its value, and use an 8-bit mask to record which bits are invalid. When it comes to multi-byte load or store operations, it is decomposed into single-byte load or store. Finally, track the undef variables used in the stored values and pointers, and replace the undef variables with new variables in each load operation to ensure correctness.
[0024] Step 3: Translation confirmation of the intelligent nuclear safety-level software compiler
[0025] First, five single-file benchmarks are selected: bzip2, gzip, oggenc, ph7 2.1.4, 6 and SQLite 3.30.1 are merged and compiled with the '-O3' optimization level; oggenc, ph7 and SQLite are batch optimized to reduce the total verification time; during the compilation process, the intermediate representation IR file pairs generated by each function before and after each optimization pass are extracted, and these IR file pairs are analyzed using the Z3 solver to obtain the impact of different timeouts on the benchmark running time;
[0026] Then, the translation verification framework MLIR was proposed and used. It optimizes and accelerates the development of various compilers and programming tools by supporting multi-level intermediate representations, reducing high-level tensor operations to fast machine instructions. An intermediate representation IR file is expressed in one or more dialects and eventually reduced to the input language of the low-level code generation framework. The tensor and tosa (Tensor Operator Set Architecture) dialects define tensor types and operations. Tensor types consist of element types and dimensions. The memref dialect enables the compiler to effectively manage memory and optimize program performance, while supporting complex memory access patterns and maintaining efficiency and security.
[0027] Use MLIR-TV to encode floating-point arithmetic and reduction operations into SMT and verify whether the semantics of the input program are preserved after conversion; when processing signed zeros, non-numbers, and negative infinity characters, fix the problem by padding the tensor; if the verification is successful, the running time of different tensors under different encodings and solver configurations is obtained; if the verification fails, MLIR-TV limits the size of the dynamic shape tensor and attempts to verify it again.
[0028] Furthermore, the constraints that the automaton described in step 1.2 must satisfy are: all states in the same automaton have different names; the states in the transfer function are all in Q; there is one and only one starting state; there can be multiple terminal states; the edges on the automaton are the transitions between states, and the calculation of the transitions begins with the calculation of its initial Boolean condition; if the calculation result is false, no other operations are performed; if the calculation result is true, the subsequent conditions are calculated in parallel according to the arrival of the input data.
[0029] Furthermore, the sequential operators described in step 1.3 are sequential operators whose semantics depend on the past value of the stream to which they are applied, i.e., the operators cannot be used in expressions associated with internal constant declarations.
[0030] Furthermore, the operator described in step 1.4, when the input variables of the operator are greater than the number of parameters actually passed in, the multiple parameters are grouped into a list and then expanded into individual elements to match the operator's requirements; the individual elements and other independent parameters are actual parameters, which are matched to the operator's input variables based on quantity, type, clock, and initialization; when an input variable of the operator is declared as a clock identifier, only a valid clock identifier can be used as its actual parameter.
[0031] Furthermore, the translation verification framework MLIR described in step 3 is an SMT encoding that promotes modular development of domain-specific compilers by sharing reusable parts; MLIR-TV is a tool for verifying and testing the correctness of MLIR conversions, providing a mechanism to ensure that the semantics of the converted code are consistent with the semantics of the original code.
[0032] Further, in the dialect described in step 3, the memref dialect is a type used for memory references, which is not just a simple pointer, but also contains detailed information about the memory layout, so that the compiler can effectively manage memory and optimize program performance; %arg0 and %arg1 are two memref parameters with different layout mappings; %arg0 has the default row-major layout mapping, and %arg1 has a column-major layout mapping.
[0033] The present invention can be used to verify existing compilers without modifying the compiler source code or redesigning the compiler structure; it can support multi-level intermediate representations, that is, programs can be operated at different abstraction levels, from high-level domain-specific languages to low-level hardware approximation codes; it can define custom operations and types, and easily integrate new language features and optimization steps; it can quickly build new compiler components, and by reusing existing components and frameworks, greatly reducing development time and difficulty. BRIEF DESCRIPTION OF THE DRAWINGS
[0034] Figure 1 Schematic diagram of some keywords or reserved words in SynLong;
[0035] Figure 2 A partial definition diagram of the state machine in SynLong;
[0036] Figure 3 A syntax diagram for a partial temporal or temporal operator;
[0037] Figure 4 Define graphs internally for the compiler;
[0038] Figure 5 A semantic diagram of the selected instruction;
[0039] Figure 6 Impact diagram for changing the SMT solver timeout for the single-file benchmark;
[0040] Figure 7 Dialect diagram for tensors and buffers in MLIR;
[0041] Figure 8 Validity case test diagram for encoding unsafe arithmetic properties of reduction operations;
[0042] Fig. 9 A performance comparison chart of hash encoding and multiset encoding. DETAILED DESCRIPTION
[0043] The present invention is described in detail below with reference to the accompanying drawings.
[0044] The specific technical solution of the present invention comprises the following steps:
[0045] Step 1: Construct the semantic model of the intelligent nuclear safety-level software compiler--Synlong operational semantics
[0046] First, the specification of the trusted code generator source language SynLong is introduced, including lexical specifications and grammatical specifications. The lexical specifications include descriptions of keywords, identifiers, constants, operators, and comments, and the grammatical specifications include descriptions of types, operators, expressions, and automata. Then, the formal semantics and design ideas of the state machine are elaborated in detail. Finally, the formal semantics of some commonly used operators in the grammar are given, and the calculation process of the operators is written.
[0047] SynLong Specification
[0048] The lexical structure and grammar of SynLong are based on the synchronous data flow language Lustre, and some content is extended on it to make it more suitable for the nuclear power field.
[0049] The SynLong lexical part defines keywords, identifiers, constants, operators and delimiters, prefix operators, whitespace, and comments. Figure 1 , which are the definitions of some keywords or reserved words in SynLong. These words will be recognized as keywords rather than variables or constants during compilation and parsing.
[0050] SynLong's syntax specification defines programs and packages, types, global flows, user-defined operators, operator bodies, and expressions. Among them, the most important parts are the state machine (belonging to the operator body part) and the operator (belonging to the expression part).
[0051] For a partial definition of the state machine, see Figure 2 , where the automaton keyword declares a state machine, and the state machine name (IDENT) can be customized by the user; state_decl represents the state declaration in the state machine, and each state machine can contain multiple states; in the state declaration, the state modified by the initial keyword indicates that the state is the initial state, and the state modified by the final keyword indicates that the state is the final state. The state keyword declares a state, and the unless and until keywords describe the transition in the state machine.
[0052] Formal Semantics of State Machines
[0053] To give the formal semantics of a state machine, we first need to understand the definition of the state machine syntax. Figure 2 The state_decl statement is a description of the state in the automaton. The meanings of each part are as follows:
[0054] -initial: Indicates that the current state is the starting state of the automaton. There can only be one starting state modified by initial in an automaton.
[0055] -final: Indicates that the current state is the final state of the automaton. Multiple states are allowed to be final states in an automaton (the starting state modified by initial can also be a final state).
[0056] -state: A keyword that declares the state of an automaton, indicating that it is a state of the automaton.
[0057] -IDENT: The name of the automaton state.
[0058] -unless: marks the subsequent transition as a strong transition. Strong transition: When the transition conditions are met, the source state of the current cycle is no longer active, and the target state becomes active.
[0059] -data_def: data definition module.
[0060] -until: marks the subsequent transition as a weak transition. Weak transition: When the transition conditions are met, the source state of the current cycle remains active, while the target state becomes inactive.
[0061] -synchro: indicates a synchronous migration.
[0062] Therefore, a six-tuple is used to represent the current automaton:
[0063] A=(Q, ∑, δ, q0, F, V)
[0064] Among them, Q is the state set, ∑ is the input symbol, δ is the transition function, q0 is the initial state, F is the set of terminal states, and V is the variable defined in the automaton.
[0065] The elements in Q are the state names of the automaton; the elements in ∑ are the input symbols (which do not seem to play such a big role here); the elements in δ are the migration edges of the automaton, which are four-tuples (q src ,q des , condition, type), which respectively represent the starting point of the edge, the end point of the edge, the condition for the edge migration and the type of migration; q0 is the initial state; the elements of F are all the terminal states; the elements of V are the variables defined in the automaton.
[0066] The constraints that the automaton needs to satisfy are:
[0067] 1) All states in the same automaton have different names:
[0068]
[0069] 2) The states in the transfer function are all in Q:
[0070]
[0071] 3) There is only one starting state:
[0072]
[0073] 4) There can be multiple terminal states:
[0074]
[0075] According to the above constraints and syntax, the operational semantics of the state description part of state_decl can be written. Since keywords such as final are optional, the possible results of this syntax will have many different usage situations, corresponding to different operational semantics. For a more concise and clear description, state_decl can be divided into four parts, and the corresponding operational semantics can be written for each part. The parts that are not affected when writing the operational semantics are represented by...
[0076] 1.[initial][final]state IDENT
[0077]
[0078] 2. Unless and until
[0079]
[0080] 3. data_def
[0081]
[0082] 4. Synchro
[0083]
[0084] -transition: represents the edge on the automaton, that is, the transition between states. The calculation of the transition begins with the calculation of its initial Boolean condition expr:
[0085] If the calculation result is false, no further operation is performed, that is, it is skipped.
[0086] If the evaluation result is true, the subsequent condition of its fork part (if any) is evaluated in parallel according to classic data flow semantics.
[0087] This means that if the conditions contain internal state (i.e. when using the times operator), that state is modified accordingly. Since the else part is not mandatory, a combination of fork transitions may be triggerable after evaluating their conditions. If one of the conditions evaluates to true, the transition is triggerable. If it is triggered (i.e. if it is the first triggerable in the program), its corresponding action is evaluated. Therefore, evaluating a fork transition is equivalent to evaluating a sequence of transitions whose conditions all follow from the conditions involved in the fork.
[0088]
[0089] Among them, q PQR It is defined by IDENT in state_decl, while the last three elements of the migration are obtained in transition.
[0090] -arrow: Simple combination, no operational semantics.
[0091] -fork: specifies the target node, migration condition, and migration type of the automaton migration edge.
[0092]
[0093] To give a simple example of a state machine, define a motor with a rotor made of a magnet rotating inside a fixed stator. The stator has two windings called a and b. When current passes through a winding, a magnetic field will be generated in either direction, acting on the rotor. The windings are energized in the order of A+ / B+, A- / B+, A- / B-, and A+ / B-, causing the rotor to move 90° clockwise from the upper right.
[0094] Table 1 Winding speed
[0095]
[0096] It counts in given increments starting from 0. It will be instantiated later in the invention to measure the duration of a motor phase. The node named count_up maps the input stream iI1 to the output stream iO1.
[0097] node count_up(iI1:int16)return(iO1:int16)
[0098] Its body contains an equation that defines iO1 to be 0, the clock operator fby, and the point-by-point addition of iI1.
[0099] The body of this node contains a switch block: the value of the input determines which set of equations is applied at each instant to define the output signal. The previous values of mA and mB are accessed by declaring them with initial last values, and writing last mA and last mB. When step is true, go to the next stage, otherwise stay in the current stage.
[0100] node count_up(iI1:int16)return(iO1:int16)
[0101]
[0102]
[0103] Formal semantics of basic operators
[0104] The present invention defines the following predicates.
[0105] Indicates that the expression e takes v in the i-th basic clock cycle.
[0106] It means that the variable x takes v in the i-th basic clock cycle.
[0107] Express the expression e, taking u to be the value most recently defined before the i-th basic clock cycle. If there is no such defined value, u0 is used instead. With this judgment, the fby semantics can be formally described.
[0108] Take, for example, the temporal operators, which are sequential operators: their semantics depend on the past values of the stream to which they are applied. Therefore, these operators cannot be used in expressions associated with inner constant declarations.
[0109] for example, Figure 3 Give the grammar of some timing or temporal operators.
[0110] According to the grammatical definition, the present invention provides the operational semantics of the first three operators ('pre', 'fby' and '->') as follows:
[0111] pre expr: Operator pre is a sequential primitive that moves the stream backwards at the last moment when it is defined in the same scope. Hence, it produces an undefined value called nil at its first activation moment.
[0112]
[0113]
[0114] expr1 fby expr2: The fby primitive is equivalent to the ->pre operation, computing the left-hand argument in the first cycle and the right-hand argument in the remaining cycles.
[0115]
[0116] expr1->expr2:->The primitive evaluates its left argument at the first instant of evaluation or after a restart, and its right argument otherwise.
[0117]
[0118] Formal semantics of higher-order operators
[0119] User-defined operators can be used as constructs in their scope. Synlong also provides higher-order primitives that modify the behavior of operators: they take an operator as input and return another operator, so they can be applied as usual.
[0120] Calls a function operator with a possibly empty argument list. The operator may have fewer arguments than input variables, since some of the arguments may be grouped into lists that are expanded into their elements. These individual elements and the remaining arguments are actual parameters, which must match the operator's input variables in terms of number, type, clocking, and initialization. Furthermore, when an input variable of this operator is declared as a clock identifier, only a valid clock identifier may be used as its actual parameter.
[0121] The formal semantics of some higher-order operators are shown below.
[0122] 1.map op<>: Assume that op is an operator that takes n parameters as input and produces m outputs. A1,...,An are arrays of size size, and the elements of the arrays are the corresponding primitive types as the input of the operator op.
[0123]
[0124] 2.mapi op<>: The operation is the same as map, but the operator op requires an additional integer parameter as the first input. The current iteration index depends on the first parameter.
[0125]
[0126] Step 2: Encoding Semantics in SMT
[0127] Figure 4Defines values, register files, and program status. The program status consists of register files, memory, and a flag indicating whether the program has executed UB. The program status is updated after each instruction is executed. (UB refers to all forms of undefined behavior)
[0128] The register file assigns a value to each register, which may be poisson (invalid data) or a set of integers, floating-point numbers, or pointers. If the value is undef, it means that its value is uncertain, that is, a non-single set, representing possible multiple values. When a value is used, an element in the set will be selected non-deterministically. Memory is a mapping from block ID to its attributes, including the block's data, size, alignment, whether it is active, etc. A new block is obtained for each allocation. (poisson and undef are forms of UB, poisson is delayed UB, and undef is immediate UB)
[0129] in Figure 4 In the table, Num(sz) refers to the size of the number; BlockID refers to the block ID; Offset refers to the offset; Pointer represents a pointer; DefinedValue refers to a defined value; Value refers to a value; Aggregate refers to an aggregate value; Memory refers to memory; RegFile refers to a register file; State refers to a state; ValueNoRet refers to no return value; FinalState refers to the final state; Int is an integer; Float is a floating point number; list Value is a value list; MemBlock is a memory block; string is a string; bool is a Boolean value; noreturn represents no return; ValueNoRet refers to no return value.
[0130] The present invention Figure 5 The semantics of several example instructions are given in. The triple S =<R,M,b> Symbols for program status (register file, memory, UB flag) Define the resulting state S' of executing instruction i in state S.
[0131] The final state is similar to register assignments, but includes the symbol noreturn. A function can end with a call to a function that does not return (e.g., exit). This is a code size optimization because it enables the compiler to skip inserting code to clean up the stack and return to the callee.
[0132] Using SMT encoding memory
[0133] In a program, memory is allocated in memory blocks. Each stack variable and global variable gets a separate memory block, and a call to the malloc function creates a new memory block. Each memory block has a unique non-negative integer identifier (bid) that is used to identify and reference the memory block.
[0134] (malloc refers to the dynamic memory allocation function, bid refers to BlockID);
[0135] After loop unrolling, the maximum number of memory blocks that the program can touch is statically calculated in order to limit the number of bits needed to encode the bid, taking into account the number of pointer inputs, stack and global variables, and instructions that may return new pointers.
[0136] In the program, a pointer is defined as a tuple (bid, off), where bid is the identifier of the memory block and off is the offset. A bit vector is generated by concatenating bid and off. If the pointer may alias multiple memory blocks (i.e., the possibility of pointing to multiple blocks), the two parts can be represented as SMT variables (for symbolic solvers). The null pointer null is defined as (0,0), pointing to an empty memory block of size 0. undef refers to an undefined pointer represented as (β,ω), where β, ω are new variables.
[0137] The pointer arithmetic instruction (gep ptr, i) returns a new pointer (the block identifier bid remains unchanged) by adding i to the offset of the pointer ptr. If the gep instruction has the inbounds attribute, an out-of-bounds check is performed when it is executed. If the base pointer or the result pointer exceeds the boundary of the memory block, the result will be marked as poison (i.e. invalid data). (The inbounds attribute is used to indicate that certain operations (such as pointer arithmetic) will not access memory out of bounds).
[0138] Each memory block has some associated attributes that describe various characteristics of the memory block, such as size, alignment, whether it is read-only, whether it is active, allocation type, and physical address. In order to encode the value of the memory block, an SMT array from pointer to byte is used. That is, the contents of the memory block are represented as a mapping from pointer to byte. There are three possible types of bytes: poison, pointer, and non-pointer. Poison means that the data of the byte is invalid or undefined; poison means that the byte stores a pointer; non-pointer means that the byte stores non-pointer data. For non-pointer bytes, an 8-bit bit vector is used to store its value, and an 8-bit mask is used to record which bits are poison (invalid bits). Floating-point numbers are converted to bit vectors when stored so that they can be stored and operated in memory. This conversion allows floating-point numbers to be processed as bit-level representations in the memory model.
[0139] When potentially multi-byte loads or stores are involved, they are broken down into single-byte loads or stores. These single-byte results are then assembled into values of the expected type (and size), which may not necessarily be a multiple of bytes.
[0140] The result of a load is poisonous if any of the following is true: (1) any of the bytes loaded is poisonous; (2) the bytes loaded are of inconsistent types (for example, one byte represents a pointer and the other represents an integer); or (3) the type of the load does not match the type of the bytes stored (for example, an attempt is made to load a pointer from a block storing integers).
[0141] The memory keeps track of undef variables used in stored values and pointers. These undef variables must be replaced with new variables on each load operation to ensure correctness. Undefined behavior (UB) is triggered if a memory access uses an out-of-bounds pointer or accesses a memory block that has been freed. UB is also triggered if a store operation is attempted on a read-only memory block.
[0142] The free(ptr) operation updates the survival status of the corresponding memory block. If the released memory block has been released, or the memory block is not allocated on the heap, or the offset of ptr is not zero, UB will be triggered.
[0143] Step 3: Translation Confirmation of Intelligent Nuclear Safety-Grade Software Compiler
[0144] The present invention selects five single-file benchmarks: bzip2, gzip, oggenc, ph7 2.1.4, 6 and SQLite3.30.1 merge, and compiles using the '-O3' optimization level, while using the '-fno-strict-aliasing' flag to disable type-based alias analysis, which allows the compiler to be more relaxed when handling pointers and references, thereby avoiding potential problems caused by alias analysis. During the compilation process, the intermediate representation (IR) file pairs generated by each function before and after each optimization pass are extracted. These file pairs can be used to analyze the impact of the optimization process on the code. When using the Z3 solver to analyze these IR file pairs, in order to control the experimental time and resource consumption, the maximum running time of each call to Z3 is limited to 1 minute and the memory usage of Z3 is limited to 1GB.
[0145] The present invention batches optimizations for oggenc, ph7, and SQLite to reduce the total verification time. To improve efficiency, some optimizations are batched together, that is, between two unsupported optimizations, all supported transformations are batched together for verification. Although batching can significantly reduce verification time, there is also a certain risk that batching may mask certain errors, for example, an optimization may accidentally fix an error introduced by a previous optimization. This means that an error in a certain optimization may not be detected because it is fixed by a subsequent optimization, thereby hiding a compilation error.
[0146] Figure 6 The third chart shows the impact of different timeouts on the benchmark run time. The run times in the chart have been normalized relative to the time taken by the benchmark when the timeout is set to 1 minute. In other words, the run time of each benchmark is based on the run time when the timeout is set to 1 minute, showing the relative changes under different timeouts.
[0147] To better understand the impact of SMT solver timeout, we ran experiments on a single-file benchmark with timeouts ranging from 1 second to 5 minutes. Figure 6 As shown, the impact on the verification result becomes limited after the timeout reaches 1 minute.
[0148] At the same time, in order to perform translation confirmation on the system based on the SMT solver, the present invention proposes a translation verification framework MLIR, which is an SMT encoding tailored for translation verification, which is an over-approximation of FP algorithms and reduction operations. MLIR is a compiler framework that promotes modular development of domain-specific compilers by sharing IR and related transformations, mainly reducing high-level tensor operations to fast machine instructions. In order to increase the speed of training and inference, several optimization methods are used. The layout of tensors may change due to spatial locality, and loops reduced from tensor operations may be fused and offloaded to the GPU if beneficial. Any errors in the optimization may cause crashes or quietly reduce the accuracy and performance of predictions.
[0149] MLIR stands for Multi-level Intermediate Representation. The MLIR project is an open source compiler infrastructure that aims to promote modular development of domain-specific compilers by sharing reusable parts. Reusable parts include dialects and related compiler transformations. Dialects are part of the compiler intermediate representation language. In MLIR, an intermediate representation (IR) program can be expressed using one or more dialects. They are ultimately reduced to the input language of the low-level code generation framework through a first-level dialect.
[0150] The tensor and tosa (Tensor Operator Set Architecture) dialects define tensor types and operations. Pre-trained machine learning models can be downgraded to these dialects for processing via importers.
[0151] A tensor type consists of an element type and a dimension. Tensor dimensions can be dynamic and can be obtained at runtime. Elements of a tensor can be accessed through operations such as tensor.extract, provided that valid indices are used. Tensor registers do not alias each other and are in static single assignment (SSA) form. TSA provides a set of operations commonly used in deep neural networks, such as convolution or pooling.
[0152] Figure 7The @calc function in (a) receives two tensors as parameters, performs a convolution operation (using the tosa.conv2d instruction), and returns the result. The input bias and output tensors are stored in tensor type virtual registers named %bias and %res, respectively. It is important to note that different dialects (such as tensor, tosa, and arith dialects for simple arithmetic operations) can coexist in an intermediate representation (IR) program. This demonstrates the flexibility of MLIR, which can integrate operations of multiple dialects into the same IR for processing.
[0153] The 'memref' dialect has a type for memory references that is more than just a simple pointer. It also contains detailed information about the memory layout, allowing the compiler to manage memory efficiently and optimize program performance. Through layout mapping, the memref type can support complex memory access patterns while remaining efficient and safe. Figure 7 (b) shows two memref parameters with different layout mappings. %arg0 has the default row-major layout map, while %arg1 has a column-major layout map. This means that the position offset of %arg1[i][j] is (i+j×3) from the reference point. Here, the values multiplied by the offsets d0 and d1 (which correspond to 1 and 3 in the #col_major mapping, respectively) are called strides. The strides determine the distance between consecutive elements in memory and are an important part of the layout map.
[0154] Figure 7 (c) shows how the operation ‘%C + %A × %B’ on three matrices ‘%A’, ‘%B’, and ‘%C’ can be represented in ‘linalg.generic’. The shapes of ‘%C’ and the result of the operation (‘%output’) must be the same. ‘linalg.generic’ is a structure containing a triple nested loop with three induction variables ‘d0’, ‘d1’, ‘d2’. ‘indexing_maps’ describes the elements retrieved from the tensors in each iteration. These retrieved elements are assigned to the block parameters %’a’, ‘%b’, and ‘%c’ in the loop body. The loop body first performs integer multiplication (arith.muli), then addition (arith.addi), and the result is passed to the next iteration, where it again becomes ‘%c’. ‘iterator_type’ shows that the third (innermost) loop is a reduction loop because it performs a summation operation, while the outer two loops can be parallelized.
[0155] linalg is the source and target dialect for several key transformations. First, tosa's operations can be reduced to combinations of linalg's operations on tensors. Second, bufferization on linalg's operations converts its tensor operands into buffers. Third, loops over 'linalg.generic' can be optimized to fused 'linalg.generic' loops or simpler operations. Fourth, transformations from linalg to lower-level dialects generate for loops (e.g. affine, scf) or control flow graphs.
[0156] MLIR-TV (MLIR Testing and Verification) is a tool for verifying and testing the correctness of MLIR transformations. It provides a mechanism to ensure that the code before and after the transformation is semantically consistent when developing and optimizing compilers. It uses Z3 and cvc5 as solvers with a timeout of 30 seconds. This invention uses Z3 because Z3 shows better performance than cvc5 in the integrity test of MLIR-TV.
[0157] Verifying that the transformed output also preserves the semantics of the input is done using MLIR-TV. To avoid timeouts, the size of dynamic shape tensors is limited to 100, but there may be some errors involving dynamic shape tensors that go undetected due to this limitation. While unsafe arithmetic properties are not currently encoded when verifying MLIR projects, this does not mean that these properties are not important in future verification. Future work may require further expansion of the verification scope to ensure that all transformations including these properties can be accurately verified, thereby improving the safety and reliability of the compiler. Through these verifications, some semantic issues in the MLIR dialect were discovered.
[0158] When handling signed zero, NaN (Not a Number), and negative infinity (-∞), the 'tosa.conv2d' and 'tosa.depthwise_conv2d' operations pad the input tensor to '+0.0' when they are lowered to 'linalg.pad_tensor'. In addition, MLIR folds the expression 'x+(+0.0)' to 'x'. However, this folding is incorrect because: (1) when 'x=-0.0', the result of 'x+(+0.0)' should be equal to 'x' (keeping '-0.0'), not '+0.0'; (2) according to the TOSA specification, floating point types (FP types) must support signed zeros, which means that '-0.0' and '+0.0' are different. Similar issues also occurred in the 'tosa.fully_connected' and 'tosa.reduce_sum' operations, which would pad the initial tensor with '+0.0' during the reduction process. Now, the 'x+(+0.0)→x' fold has been fixed.
[0159] Figure 8 The chart on the left compares the runtime of the MLIR-TV tool with different encoding and solver configurations for different test cases and input sizes. The numbers below the labels indicate the size of the input tensor. The X-axis shows the names of the different test cases (e.g. sum-RV, sum-TP, etc.) and their corresponding input tensor sizes. The Y-axis shows the MLIR-TV runtime in milliseconds. The timeout in the figure is set to 30,000 milliseconds (30 seconds). Figure 8 The icons on the right describe several test cases and their corresponding transformation operations. X is a 1D or 2D tensor, Σ is summation, rev is inverse, · is dot product, and ++ is concatenation. sum--RV: Σrev(X)→ΣX, which means summing the tensor X after reversing it, and then converting it to a direct summation. sum--TP: ΣXT→ΣX, which means summing the tensor X after transposing it, and then converting it to a direct summation. sum--CC: Σ(ΣX1,ΣX2)→Σ(X1++X2), which means summing the two tensors X1 and X2 separately, and then concatenating them for summation. dot--RV: rev(X1)·rev(X2)→X1·X2, which means doing dot products on the two tensors after reversing them separately, and then converting it to directly doing dot products on the original tensors. dot--CC: X1·X2+X3·X4→(X1++X3)·(X2++X4), which means to calculate the dot product of two tensor pairs separately, and then convert them into the concatenated dot product calculation.
[0160] For 'memref' operations and read-only memory blocks, it is not possible to find a sensible semantics for what should happen when the operand of a 'linalg.fill' operation is a 'memref' reference to a read-only memory block. If the 'linalg.fill' operation triggers undefined behavior (UB) when using a read-only 'memref', then the behavior of the 'linalg-bufferize' conversion cannot be explained, because the conversion creates a 'linalg.fill' whose 'memref' operand points to a read-only block. If this operation is defined, then the 'linalg-generalize-named-ops' conversion cannot be explained, because this conversion converts 'linalg.fill' into a loop in which values are stored into pointers.
[0161] At the same time, the present invention compares the performance of hash coding with that of multiset coding. In the latter coding, if the multisets converted from the input array are equal, the two reductions are assumed to be equal. For cvc5, the present invention uses its native multiset theory. For Z3, the multiset is simulated by defining an array that counts the number of elements. The present invention sets the QF_AUFBV logic to Z3 by default, and only uses the ALL logic when the solver fails. For cvc5, the HO_AUFBV and HO_ALL logics are used respectively. The present invention runs 10 tests and calculates their average execution time. The timeout is set to 30 seconds.
[0162] exist Fig. 9 In the graph, the X-axis represents the number of elements in the test (#Elements), and the Y-axis represents the running time in milliseconds (the graph is represented on a logarithmic scale). At the top of the graph, "Timeout" (T / O) is marked, indicating that the timeout is set to 30 seconds. Figure 8 , and found that hash encoding is faster than multiset encoding in the overall test, especially showing better performance on large inputs. cvc5's multiset theory performs better than Z3's array encoding, but is still slower than hash-based encodings.
[0163] It can be seen that the technical solution provided by the present invention first gives a semantic model, then analyzes the encoding of SMT, and finally translates and confirms the intelligent nuclear safety-level software with the help of the Z3 constraint solver, laying a certain foundation for the modeling and verification of safety-critical systems.
Claims
1. A formal verification method for an intelligent nuclear safety-level software compiler based on translation confirmation, characterized in that the method comprises the following steps: Step 1: Construct the semantic model of the intelligent nuclear safety-level software compiler, namely Synlong operational semantics 1.1: SynLong Specification SynLong's lexical structure and syntax are based on and extended from the synchronous data flow language Lustre; The lexical part of SynLong defines keywords, identifiers, constants, operators and delimiters, prefix operators, whitespace symbols, and comments; the grammatical specification of SynLong defines programs and packages, types, global streams, user-defined operators, operator bodies, and expressions; 1.2: Formal Semantics of State Machines The state machine is a special type of automaton, which is represented by a six-tuple: A = (Q, ∑, δ, q0, F, V), where Q is the state set, ∑ is the input symbol, δ is the transition function, q0 is the initial state, F is the set of terminal states, and V is the variable defined in the automaton; 1.3: Formal semantics of basic operators definition These predicates, and based on the grammatical definition, write the semantics of the 'pre', 'fby', and '->' operators in the temporal operator; 1.4: Formal semantics of higher-order operators Synlong provides high-level primitives for modifying operator behavior: the input consists of a function operator and a variable-length empty argument list, and the output is another operator; Step 2: Encoding Semantics in SMT First, define the value, register file, and program state; then the register file assigns a value to each register. Depending on the value, when the value is used, an element in the set will be non-deterministically selected, and a new block will be obtained for each assignment; the final state is the register assignment, which contains the symbol noreturn, which means no return; the function ends with a call to a function that does not return; Use the malloc function to let each stack variable and global variable obtain an independent memory block. Each memory block has a unique non-negative integer identifier bid, which is used to identify and reference the memory block. After the loop is unrolled, the maximum number of memory blocks that the program can access is statically calculated to limit the number of bits required to encode the bid. A pointer is defined as a tuple (bid, off). Pointer arithmetic instructions return a new pointer by changing the offset. A SMT array from pointer to byte is used to encode the value of the memory block, that is, the content of the memory block is represented as a mapping from pointer to byte. There are three types of bytes: poison, pointer, and non-pointer. For non-pointer bytes, an 8-bit bit vector is used to store its value, and an 8-bit mask is used to record which bits are invalid. When multi-byte load or store operations are involved, they are decomposed into single-byte load or store operations. Finally, the undef variables used in the stored values and pointers are tracked, and the undef variables are replaced with new variables in each load operation to ensure correctness. Step 3: Translation confirmation of the intelligent nuclear safety-level software compiler First, five single-file benchmarks were selected: bzip2, gzip, oggenc, ph7 2.1.4, 6, and SQLite 3.30.1 were merged and compiled with the '-O3' optimization level; oggenc, ph7, and SQLite were batch optimized to reduce the total verification time; During the compilation process, we extract the intermediate representation IR file pairs generated by each function before and after each optimization pass, and use the Z3 solver to analyze these IR file pairs to find out the impact of different timeouts on the benchmark run time; Then, we use the translation verification framework MLIR to optimize and accelerate the development of various compilers and programming tools by supporting multi-level intermediate representations, reducing high-level tensor operations to fast machine instructions. An intermediate representation IR file is expressed in one or more dialects and eventually reduced to the input language of the low-level code generation framework. The tensor and tosa dialects define tensor types and operations. Tensor types consist of element types and dimensions. The memref dialect enables the compiler to effectively manage memory and optimize program performance, while supporting complex memory access patterns and maintaining efficiency and security. Use MLIR-TV to encode floating-point arithmetic and reduction operations into SMT and verify whether the semantics of the input program are preserved after conversion; when processing signed zeros, non-numbers, and negative infinity characters, fix the problem by padding the tensor; if the verification is successful, the running time of different tensors under different encodings and solver configurations is obtained; if the verification fails, MLIR-TV limits the size of the dynamic shape tensor and attempts to verify it again.
2. The formal verification method for an intelligent nuclear safety-level software compiler based on translation confirmation as claimed in claim 1, characterized in that: The constraints that the automaton described in step 1.2 must satisfy are: all states in the same automaton have different names; all states in the transition function are in Q; there is only one starting state; there can be multiple terminal states; the edges on the automaton are the transitions between states, and the calculation of the transitions begins with the calculation of its initial Boolean condition; If the calculation result is false, no other operations will be performed; if the calculation result is true, the subsequent conditions will be calculated in parallel according to the arrival of the input data.
3. The formal verification method of the intelligent nuclear safety-level software compiler based on translation confirmation as claimed in claim 1, characterized in that: The sequential operators described in step 1.3 are sequential operators whose semantics depend on the past value of the stream to which they are applied, that is, the operators cannot be used in expressions associated with internal constant declarations.
4. The formal verification method of the intelligent nuclear safety-level software compiler based on translation confirmation as claimed in claim 1, characterized in that: For the operator described in step 1.4, when the input variables of the operator are more than the number of parameters actually passed in, multiple parameters are grouped into a list and then expanded into individual elements to match the operator's requirements; the individual elements and other independent parameters are actual parameters, which are matched to the input variables of the operator based on quantity, type, clock, and initialization; when an input variable of the operator is declared as a clock identifier, only a valid clock identifier can be used as its actual parameter.
5. The formal verification method for an intelligent nuclear safety-level software compiler based on translation confirmation as claimed in claim 1, characterized in that: The translation verification framework MLIR described in step 3 is an SMT encoding that promotes modular development of domain-specific compilers by sharing reusable parts; MLIR-TV is a tool for verifying and testing the correctness of MLIR conversions, providing a mechanism to ensure that the semantics of the converted code are consistent with the semantics of the original code.
6. The method for formal verification of an intelligent nuclear safety-level software compiler based on translation confirmation as claimed in claim 1, characterized in that: The dialect described in step 3, memref dialect is a type used for memory references. It is not just a simple pointer, but also contains detailed information about the memory layout, allowing the compiler to effectively manage memory and optimize program performance; %arg0 and %arg1 are two memref parameters with different layout mappings; %arg0 has the default row-major layout mapping, and %arg1 has a column-major layout mapping.
Citation Information
Cited By
Memory access security check method for GPU compiler
CN120610908A