parent
f94c6c48cc
commit
fdb9277ad1
@ -0,0 +1,50 @@
|
||||
|
||||
OPT_FLAGS?= -march=native -flto \
|
||||
-march=native -fgraphite -fopenmp -floop-parallelize-all -ftree-parallelize-loops=4 \
|
||||
-floop-interchange -ftree-loop-distribution -floop-strip-mine -floop-block \
|
||||
-fno-stack-check -fno-strict-aliasing
|
||||
|
||||
INCLUDE?=../common/include
|
||||
|
||||
COMMON_FLAGS?=-pipe -O3 -Wall -pedantic -Wextra -Wstrict-aliasing
|
||||
|
||||
C_OPT_FLAGS=$(OPT_FLAGS)
|
||||
CXX_OPT_FLAGS=$(OPT_FLAGS) -felide-constructors -fno-exceptions
|
||||
LD_OPT_FLAGS?=-O3 -flto
|
||||
|
||||
|
||||
CFLAGS?=$(COMMON_FLAGS) $(C_OPT_FLAGS) --std=gnu11
|
||||
CXXFLAG?+=$(COMMON_FLAGS) $(CXX_OPT_FLAGS) --std=gnu++20
|
||||
LDFLAGS?=$(LD_OPT_FLAGS)
|
||||
|
||||
CFLAGS+=-I$(INCLUDE)
|
||||
|
||||
.PHONY: all
|
||||
all: part1 part2
|
||||
|
||||
.PHONY: test
|
||||
test: part1-test part2-test
|
||||
|
||||
inpu%.h: inpu%
|
||||
@rm -f $@
|
||||
while read line; do \
|
||||
echo "\"$$line\"," >> $@; \
|
||||
done < $<
|
||||
|
||||
part1: day8.c | input.h
|
||||
$(CC) $< $(CFLAGS) -o $@ $(LDFLAGS)
|
||||
strip $@
|
||||
|
||||
part2: day8.c | input.h
|
||||
$(CC) $< -DPART2 $(CFLAGS) -o $@ $(LDFLAGS)
|
||||
strip $@
|
||||
|
||||
part1-test: day8.c | input-test.h
|
||||
$(CC) $< -DTEST $(CFLAGS) -o $@ $(LDFLAGS)
|
||||
|
||||
part2-test: day8.c | input-test.h
|
||||
$(CC) $< -DPART2 -DTEST $(CFLAGS) -o $@ $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f part{1,2}{,-test}
|
||||
rm -f input{,-test}.h
|
@ -0,0 +1,162 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <attrs.h>
|
||||
#include <panic.h>
|
||||
|
||||
const char* const input[] = {
|
||||
#ifdef TEST
|
||||
#include "input-test.h"
|
||||
#else
|
||||
#include "input.h"
|
||||
#endif
|
||||
};
|
||||
|
||||
#define input_sz (sizeof(input)/sizeof(char*))
|
||||
|
||||
enum op {
|
||||
OP_NOP=0,
|
||||
OP_ACC,
|
||||
OP_JMP,
|
||||
OP_HLT,
|
||||
};
|
||||
|
||||
inline static const char* strop(enum op op)
|
||||
{
|
||||
switch(op) {
|
||||
case OP_NOP: return "nop";
|
||||
case OP_ACC: return "acc";
|
||||
case OP_JMP: return "jmp";
|
||||
case OP_HLT: return "hlt";
|
||||
}
|
||||
panic("Unknown op %d", op);
|
||||
}
|
||||
|
||||
enum exec_stat {
|
||||
END = 0,
|
||||
HALT,
|
||||
};
|
||||
|
||||
typedef struct instr {
|
||||
enum op operator;
|
||||
int64_t operand;
|
||||
} instruction_t;
|
||||
|
||||
struct reg {
|
||||
ssize_t pc;
|
||||
int64_t acc;
|
||||
int pjmp[input_sz];
|
||||
};
|
||||
|
||||
|
||||
static pure instruction_t parse_single(const char* str)
|
||||
{
|
||||
register instruction_t op;
|
||||
switch(str[0]) {
|
||||
case 'n': op.operator = OP_NOP; break;
|
||||
case 'a': op.operator = OP_ACC; break;
|
||||
case 'j': op.operator = OP_JMP; break;
|
||||
default: panic("Unknown instr op: %s", str);
|
||||
}
|
||||
op.operand = (int64_t)atoll(str+4);
|
||||
return op;
|
||||
}
|
||||
|
||||
enum exec_stat execute_destructive(instruction_t* restrict prog, struct reg* restrict reg, size_t len);
|
||||
|
||||
enum exec_stat execute(const instruction_t* prog, struct reg* restrict reg)
|
||||
{
|
||||
instruction_t dest[input_sz];
|
||||
memcpy(dest, prog, input_sz * sizeof(prog[0]));
|
||||
reg->pc = 0;
|
||||
reg->acc =0;
|
||||
return execute_destructive(dest, reg, input_sz);
|
||||
}
|
||||
enum exec_stat execute_destructive(instruction_t* restrict prog, struct reg* restrict reg, size_t len)
|
||||
{
|
||||
for(;(size_t)reg->pc<len;)
|
||||
{
|
||||
if(reg->pc < 0) panic("Invalid pc %ld", reg->pc);
|
||||
|
||||
instruction_t* restrict current = &prog[reg->pc];
|
||||
enum op op = current->operator;
|
||||
current->operator = OP_HLT;
|
||||
switch(op)
|
||||
{
|
||||
case OP_HLT: return HALT;
|
||||
case OP_JMP:
|
||||
reg->pc += (ssize_t)current->operand;
|
||||
continue;
|
||||
case OP_ACC: reg->acc += current->operand; break;
|
||||
default: break;
|
||||
}
|
||||
reg->pc += 1;
|
||||
}
|
||||
|
||||
return END;
|
||||
}
|
||||
|
||||
void swap(size_t* restrict s1, size_t* restrict s2)
|
||||
{
|
||||
*s1 = *s1 ^ *s2;
|
||||
*s2 = *s1 ^ *s2;
|
||||
*s1 = *s1 ^ *s2;
|
||||
}
|
||||
|
||||
void reverse(size_t* restrict ar, size_t len)
|
||||
{
|
||||
size_t i=0,j=len-1;
|
||||
if(j==1) return;
|
||||
while(i<j) {
|
||||
|
||||
swap(ar+i, ar+j);
|
||||
i++; j--;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
instruction_t instr[input_sz];
|
||||
size_t jumps[input_sz]; size_t nj =0;
|
||||
size_t nops[input_sz]; size_t ni=0;
|
||||
for (size_t i=0;i<input_sz;i++) {
|
||||
switch((instr[i] = parse_single(input[i])).operator)
|
||||
{
|
||||
case OP_NOP: nops[ni++] = i; break;
|
||||
case OP_JMP: jumps[nj++] = i; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
reverse(jumps, nj);
|
||||
reverse(nops, ni);
|
||||
|
||||
struct reg state = {0};
|
||||
#ifdef PART2
|
||||
register int seti=0,setj=0;
|
||||
while (execute(instr, &state)) {
|
||||
if(seti)instr[nops [ni]].operator = OP_NOP;
|
||||
if(setj)instr[jumps[nj]].operator = OP_JMP;
|
||||
|
||||
seti=setj=0;
|
||||
if(ni) {
|
||||
instr[nops [--ni]].operator = OP_JMP;
|
||||
seti=1;
|
||||
}
|
||||
else if(nj) {
|
||||
instr[jumps[--nj]].operator = OP_NOP;
|
||||
setj=1;
|
||||
}
|
||||
else panic("All inputs halt: %ld", state.acc);
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Retry: %ld %ld\n", state.pc, state.acc);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
enum exec_stat res = execute_destructive(instr, &state, input_sz);
|
||||
if(!res) panic("No halt");
|
||||
#endif
|
||||
printf("%ld\n", state.acc);
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in new issue