Skip to content

Commit

Permalink
feat(fuzzer): implement fuzzing of composite constants and fix a bug …
Browse files Browse the repository at this point in the history
…related to operand finding
  • Loading branch information
rayanht committed Apr 17, 2022
1 parent b71a75e commit 07c06c4
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 93 deletions.
30 changes: 21 additions & 9 deletions langspec/opcodes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,14 @@
"to_spasm",
"validate_opcode",
"fuzz",
"get_base_type",
]


class ReparametrizationError(Exception):
...


def members(inst):
return tuple(
[
Expand Down Expand Up @@ -123,8 +126,8 @@ def get_parametrization(cls):
def set_zero_probability(cls, target_cls) -> None:
# There is a tricky case here when an OpCode can be reached
# from multiple delegators.
#
# The delegation path then has a fork in it and when we try
#
# The delegation path then has a fork in it and when we try
# to reparametrize it is possible that ot all delegators in
# the path have been parametrized yet
PARAMETRIZATIONS[cls.__name__][target_cls.__name__] = 0
Expand All @@ -151,9 +154,13 @@ def fuzz(self, context: "Context") -> List[OpCode]:
for sub in subclasses
]
try:
return [*random.choices(subclasses, weights=weights, k=1)[0]().fuzz(context)]
return [
*random.choices(subclasses, weights=weights, k=1)[0]().fuzz(context)
]
except ReparametrizationError:
return [*random.choices(subclasses, weights=weights, k=1)[0]().fuzz(context)]
return [
*random.choices(subclasses, weights=weights, k=1)[0]().fuzz(context)
]


class FuzzLeaf(OpCode):
Expand All @@ -162,24 +169,29 @@ def fuzz(self, context: "Context") -> List[OpCode]:


class Type(FuzzDelegator):
pass
def get_base_type(self):
...


class Constant(FuzzDelegator):
type: Type

def get_base_type(self):
return self.type.get_base_type()


class Statement(FuzzDelegator):
pass
def get_base_type(self):
return self.type.get_base_type()


class Untyped:
pass
...


class Signed:
pass
...


class Unsigned:
pass
...
18 changes: 14 additions & 4 deletions langspec/opcodes/arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def find_arithmetic_operands(
if isinstance(operand1.type, OpTypeVector):
operand2 = random.choice(
list(
filter(lambda op: isinstance(op.type, OpTypeVector), eligible_operands)
filter(lambda op: isinstance(op.type, OpTypeVector) and len(op.type) == len(operand1.type), eligible_operands)
)
)
elif isinstance(operand1.type, NumericalType):
Expand All @@ -60,7 +60,7 @@ def find_arithmetic_operands(
T = TypeVar("T")

class ArithmeticOperator(Statement, Generic[T]):
pass
...

class UnaryArithmeticOperatorFuzzMixin:
def fuzz(self, context: "Context") -> List[OpCode]:
Expand All @@ -69,7 +69,12 @@ def fuzz(self, context: "Context") -> List[OpCode]:
if not operands:
return []
self.operand: Statement = operands[0]
self.type = self.operand.type
if isinstance(self.operand.type, OpTypeVector):
self.type = OpTypeVector().fuzz(context)[-1]
self.type.type = self.operand.type.type
self.type.size = len(self.operand.type)
else:
self.type = self.operand.type
return [self]


Expand All @@ -81,7 +86,12 @@ def fuzz(self, context: "Context") -> List[OpCode]:
return []
self.operand1 = operands[0]
self.operand2 = operands[1]
self.type = self.operand1.type
if isinstance(self.operand1.type, OpTypeVector):
self.type = OpTypeVector().fuzz(context)[-1]
self.type.type = self.operand1.type.type
self.type.size = len(self.operand1.type)
else:
self.type = self.operand1.type
return [self]


Expand Down
94 changes: 55 additions & 39 deletions langspec/opcodes/constants.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from copy import deepcopy
import random
from typing import TYPE_CHECKING, List, Union
from typing import TYPE_CHECKING, List, Tuple, Union

from langspec.opcodes import (
Constant,
Expand All @@ -9,11 +10,18 @@
if TYPE_CHECKING:
from langspec.opcodes.context import Context
from langspec.opcodes.types.abstract_types import (
ContainerType,
NumericalType,
Type,
)
from langspec.opcodes.types.concrete_types import (
OpTypeArray,
OpTypeBool,
OpTypeFloat,
OpTypeInt,
OpTypeMatrix,
OpTypeStruct,
OpTypeVector,
)
from patched_dataclass import dataclass

Expand Down Expand Up @@ -42,50 +50,58 @@ class OpConstant(Constant):
def fuzz(self, context: "Context") -> List[OpCode]:

self.type = NumericalType().fuzz(context)[0]
from langspec.opcodes.types.concrete_types import OpTypeInt

if isinstance(self.type, OpTypeInt):
if self.type.signed:
self.value = random.randint(
-(2 ** (self.type.width - 1)), 2 ** (self.type.width - 1) - 1
)
self.value = random.randint(-100, 100)
else:
self.value = random.randint(0, 2 ** self.type.width)
self.value = random.randint(0, 100)
else:
self.value = random.uniform(0, 2 ** self.type.width)
self.value = random.uniform(0, 100)

return [self.type, self]


# @dataclass
# class OpConstantComposite(Constant):
# constants: Tuple[Constant]

# def fuzz(self, tvc, context: Context) -> List[OpCode]:
#
# self.type: Union[MixedContainerType, UniformContainerType] = random.choice(
# list(
# filter(
# lambda tvc: isinstance(tvc, ContainerType)
# and not isinstance(tvc, OpTypePointer),
# TVC_table.keys(),
# )
# )
# )
# self.constants = []
# if isinstance(self.type, UniformContainerType):
# for _ in range(len(self.type)):
# constant: Constant = find_constant(tvc, self.type.type, context)
# self.constants.append(constant)
# else:
# for type in self.type.types:
# constant: Constant = find_constant(tvc, type, context)
# self.constants.append(constant)
# self.constants = tuple(self.constants)
# return [self]

# def to_spasm(self, TVC_table) -> str:
# spasm = f"%{self.id} = OpConstantComposite %{TVC_table[self.type]}"
# for constant in self.constants:
# spasm += f" %{TVC_table[constant]}"
# return spasm
class OpConstantComposite(Constant):
type: Type = None
constituents: Tuple[OpCode] = None

def fuzz(self, context: "Context") -> List[OpCode]:
composite_type = random.choice(
[OpTypeArray, OpTypeVector] #, OpTypeMatrix]
)().fuzz(context)
self.type: OpTypeArray | OpTypeVector = composite_type[-1]
base_type: Type = self.type.get_base_type()
self.constituents = []
if isinstance(base_type, OpTypeBool):
for _ in range(len(self.type)):
entry = random.choice([OpConstantTrue, OpConstantFalse])().fuzz(
context
)
composite_type += entry[:-1]
self.constituents.append(entry[-1])
elif isinstance(base_type, OpTypeInt):
for _ in range(len(self.type)):
entry = OpConstant().fuzz(context)
while not isinstance(entry[-1].type, OpTypeInt):
entry = OpConstant().fuzz(context)
new_type = deepcopy(entry[-1].type)
new_type.width = base_type.width
new_type.signed = base_type.signed
if not new_type.signed:
entry[-1].value = abs(entry[-1].value)
entry[-1].type = new_type
composite_type.append(new_type)
self.constituents.append(entry[-1])
elif isinstance(base_type, OpTypeFloat):
for _ in range(len(self.type)):
entry = OpConstant().fuzz(context)
while not isinstance(entry[-1].type, OpTypeFloat):
entry = OpConstant().fuzz(context)
new_type = deepcopy(entry[-1].type)
new_type.width = base_type.width
entry[-1].type = new_type
composite_type.append(new_type)
self.constituents.append(entry[-1])
self.constituents = tuple(self.constituents)
return [*composite_type, *self.constituents, self]
24 changes: 9 additions & 15 deletions langspec/opcodes/conversions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
)
from langspec.opcodes import OpCode, Signed, Statement, Unsigned

from langspec.opcodes.constants import Constant
from langspec.opcodes.constants import Constant, OpConstantComposite

if TYPE_CHECKING:
from langspec.opcodes.context import Context
Expand Down Expand Up @@ -41,17 +41,9 @@ def find_conversion_operand(
) + context.get_constants((OpTypeInt, OpTypeVector))
eligible_operands: List[Operand] = []
for operand in operands:
if isinstance(operand.type, target_type) or (
isinstance(operand.type, OpTypeVector)
and isinstance(operand.type.type, target_type)
):
if (
isinstance(operand.type, NumericalType)
or isinstance(operand.type, OpTypeVector)
and isinstance(operand.type.type, NumericalType)
):
if signed is not None and operand.type.signed != signed:
continue
if isinstance(operand.get_base_type(), target_type):
if signed is not None and operand.get_base_type().signed != signed:
continue
eligible_operands.append(operand)
if len(eligible_operands) == 0:
return None
Expand Down Expand Up @@ -88,12 +80,14 @@ def fuzz(self, context: "Context") -> List[OpCode]:
inner_type = destination_type().fuzz(context)[-1]
if destination_signed is not None:
inner_type.signed = destination_signed
inner_type.width = self.operand.type.width
context.tvc[inner_type] = inner_type.id
if isinstance(self.operand, OpTypeVector):
# if isinstance(self.operand, OpConstantComposite):
# inner_type.width = self.operand.type.type.width
inner_type.width = self.operand.get_base_type().width
if isinstance(self.operand.type, (OpConstantComposite, OpTypeVector)):
self.type = OpTypeVector().fuzz(context)[-1]
self.type.type = inner_type
self.type.size = self.operand.size
self.type.size = len(self.operand.type)
context.tvc[self.type] = self.type.id
else:
self.type = inner_type
Expand Down
32 changes: 18 additions & 14 deletions langspec/opcodes/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,17 @@ def find_logical_operands(
) + context.get_constants((OpTypeBool, OpTypeVector))
eligible_operands: List[Operand] = []
for operand in operands:
if isinstance(operand.type, target_type) or (
isinstance(operand.type, OpTypeVector)
and isinstance(operand.type.type, target_type)
):
if (
isinstance(operand.type, NumericalType)
or isinstance(operand.type, OpTypeVector)
and isinstance(operand.type.type, NumericalType)
):
if signed is not None and operand.type.signed != signed:
continue
if isinstance(operand.get_base_type(), target_type):
if signed is not None and operand.get_base_type().signed != signed:
continue
eligible_operands.append(operand)
if len(eligible_operands) == 0:
return None
operand1 = random.choice(eligible_operands)
if isinstance(operand1.type, OpTypeVector):
operand2 = random.choice(
list(
filter(lambda op: isinstance(op.type, OpTypeVector), eligible_operands)
filter(lambda op: isinstance(op.type, OpTypeVector) and len(op.type) == len(operand1.type), eligible_operands)
)
)
elif isinstance(operand1.type, NumericalType):
Expand Down Expand Up @@ -90,7 +82,13 @@ def fuzz(self, context: "Context") -> List[OpCode]:
if not operands:
return []
self.operand: Statement = operands[0]
self.type = OpTypeBool()
if isinstance(self.operand.type, OpTypeVector):
self.type = OpTypeVector().fuzz(context)[-1]
self.type.type = OpTypeBool()
self.type.size = len(self.operand.type)
else:
self.type = OpTypeBool()
context.tvc[self.type] = self.type.id
return [self]


Expand All @@ -105,7 +103,13 @@ def fuzz(self, context: "Context") -> List[OpCode]:
return []
self.operand1 = operands[0]
self.operand2 = operands[1]
self.type = OpTypeBool()
if isinstance(self.operand1.type, OpTypeVector):
self.type = OpTypeVector().fuzz(context)[-1]
self.type.type = OpTypeBool()
self.type.size = len(self.operand1.type)
else:
self.type = OpTypeBool()
context.tvc[self.type] = self.type.id
return [self]


Expand Down
12 changes: 6 additions & 6 deletions langspec/opcodes/types/abstract_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@


class ScalarType(Type):
pass
...


class NumericalType(Type):
pass
...


class ContainerType(Type):
pass
...


class UniformContainerType(ContainerType):
pass
...


class MixedContainerType(ContainerType):
pass
...


class ArithmeticType(Type):
pass
...
Loading

0 comments on commit 07c06c4

Please sign in to comment.