From f34880d86174ae88188ee91b453b0b3ff2f7a914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Maseli?= Date: Sun, 22 Oct 2023 17:29:29 +0200 Subject: [PATCH 1/2] Add Mem2reg pass (#522) --- .../program/processing/MemToReg.java | 360 ++++++++++++++++++ .../program/processing/ProcessingManager.java | 2 + 2 files changed, 362 insertions(+) create mode 100644 dartagnan/src/main/java/com/dat3m/dartagnan/program/processing/MemToReg.java diff --git a/dartagnan/src/main/java/com/dat3m/dartagnan/program/processing/MemToReg.java b/dartagnan/src/main/java/com/dat3m/dartagnan/program/processing/MemToReg.java new file mode 100644 index 0000000000..98bfe9198d --- /dev/null +++ b/dartagnan/src/main/java/com/dat3m/dartagnan/program/processing/MemToReg.java @@ -0,0 +1,360 @@ +package com.dat3m.dartagnan.program.processing; + +import com.dat3m.dartagnan.expression.Expression; +import com.dat3m.dartagnan.expression.ExpressionFactory; +import com.dat3m.dartagnan.expression.IExprBin; +import com.dat3m.dartagnan.expression.IValue; +import com.dat3m.dartagnan.expression.op.IOpBin; +import com.dat3m.dartagnan.expression.type.BooleanType; +import com.dat3m.dartagnan.expression.type.IntegerType; +import com.dat3m.dartagnan.expression.type.Type; +import com.dat3m.dartagnan.program.Function; +import com.dat3m.dartagnan.program.Register; +import com.dat3m.dartagnan.program.event.EventFactory; +import com.dat3m.dartagnan.program.event.core.*; +import com.dat3m.dartagnan.program.event.core.utils.RegReader; +import com.dat3m.dartagnan.program.event.core.utils.RegWriter; +import com.dat3m.dartagnan.program.event.lang.Alloc; +import com.dat3m.dartagnan.program.event.visitors.EventVisitor; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.sosy_lab.common.configuration.Configuration; +import org.sosy_lab.common.configuration.InvalidConfigurationException; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/* + * Replaces memory accesses that involve addresses that are only used by one thread. + * Loops are allowed, this analysis ensures termination by storing local copies for each destination of a backwards jump. + * On each function, the iteration happens on the finite function lattice E -> (((R+A) -> (A+top))+bot): + * A is the finite set of allocation events. + * E is the finite set of events. + * R is the finite set of registers. + * A+top is the co-semi-lattice with new top element over A. + * (R+A) -> (A+top) is the function semi-lattice over A+1. + * ((R+A) -> (A+top))+bot is the lattice with new bottom element over (R+A) -> (A+top). + * Tracks when a register or address contains an address. + * Unifies all global addresses into one element represented by {@code null}. + * TODO also replace memcpy memset memcmp. Currently treated as calls to unknown. + * TODO Thread-local variables were already created as MemoryObject and are not removed, yet. + */ +public class MemToReg implements FunctionProcessor { + + private static final Logger logger = LogManager.getLogger(MemToReg.class); + + private MemToReg() { + } + + public static MemToReg fromConfig(Configuration config) throws InvalidConfigurationException { + return new MemToReg(); + } + + @Override + public void run(Function function) { + logger.trace("Processing function \"{}\".", function.getName()); + final Matcher info = analyze(function); + promoteAll(function, info); + } + + private Matcher analyze(Function function) { + final var matcher = new Matcher(); + // Initially, all locally-allocated addresses are potentially promotable. + for (final Alloc allocation : function.getEvents(Alloc.class)) { + // Allocations will usually not have users. Otherwise, their object is not promotable. + if (allocation.getUsers().isEmpty()) { + matcher.reachabilityGraph.put(allocation, new HashSet<>()); + } + } + // This loop should terminate, since back jumps occur, only if changes were made. + final List events = function.getEvents(); + Label back; + for (int i = 0; i < events.size(); i = back == null ? i + 1 : events.indexOf(back)) { + back = events.get(i).accept(matcher); + } + return matcher; + } + + private void promoteAll(Function function, Matcher matcher) { + final ExpressionFactory expressions = ExpressionFactory.getInstance(); + // Replace every unmarked address. + final var replacingRegisters = new HashMap>(); + for (final Alloc allocation : function.getEvents(Alloc.class)) { + if (matcher.reachabilityGraph.containsKey(allocation)) { + final List registerTypes = getPrimitiveReplacementTypes(allocation); + if (registerTypes != null) { + replacingRegisters.put(allocation, registerTypes.stream().map(function::newRegister).toList()); + boolean deleted = allocation.tryDelete(); + assert deleted : "Allocation cannot be removed, probably because it has remaining users."; + } + } + } + int loadCount = 0, storeCount = 0; + // Replace all loads and stores to replaceable storage. + for (final Map.Entry entry : matcher.accesses.entrySet()) { + final MemoryEvent event = entry.getKey(); + final AddressOffset access = entry.getValue(); + final List registers = access == null ? null : replacingRegisters.get(access.base); + if (registers == null || access.offset < 0 || access.offset >= registers.size()) { + continue; + } + if (event instanceof Load load) { + final Register reg = load.getResultRegister(); + assert load.getUsers().isEmpty(); + load.replaceBy(EventFactory.newLocal(reg, expressions.makeCast(registers.get(access.offset), reg.getType()))); + loadCount++; + } + if (event instanceof Store store) { + final Register reg = registers.get(access.offset); + assert store.getUsers().isEmpty(); + store.replaceBy(EventFactory.newLocal(reg, expressions.makeCast(store.getMemValue(), reg.getType()))); + storeCount++; + } + } + // Remove involved local assignments. + for (final Map.Entry entry : matcher.assignments.entrySet()) { + if (replacingRegisters.containsKey(entry.getValue().base)) { + assert entry.getKey().getUsers().isEmpty(); + entry.getKey().tryDelete(); + } + } + if (loadCount + storeCount > 0) { + logger.debug("Removed {} loads and {} stores in function \"{}\".", loadCount, storeCount, function.getName()); + } + } + + private List getPrimitiveReplacementTypes(Alloc allocation) { + if (!(allocation.getArraySize() instanceof IValue sizeExpression)) { + return null; + } + final int size = sizeExpression.getValueAsInt(); + if (size != 1) { + //TODO arrays + return null; + } + final List replacementTypes = new ArrayList<>(); + final Type type = allocation.getAllocationType(); + //TODO PointerType + if (type instanceof IntegerType || type instanceof BooleanType) { + replacementTypes.add(type); + } else { + //TODO aggregate types + return null; + } + //TODO check for mixed-size accesses + return replacementTypes; + } + + // Invariant: base != null + private record AddressOffset(RegWriter base, int offset) { + private AddressOffset increase(int o) { + return o == 0 ? this : new AddressOffset(base, offset + o); + } + } + + // Invariant: register != null + private record RegisterOffset(Register register, int offset) {} + + // Processes events in program order. + // Returns a label, if it is program-ordered before the current event and its symbolic state was updated. + private static final class Matcher implements EventVisitor