harfbuzz/src/graphite2/src/inc/opcodes.h

648 lines
18 KiB
C++

// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
// This file will be pulled into and integrated into a machine implmentation
// DO NOT build directly and under no circumstances ever #include headers in
// here or you will break the direct_machine.
//
// Implementers' notes
// ==================
// You have access to a few primitives and the full C++ code:
// declare_params(n) Tells the interpreter how many bytes of parameter
// space to claim for this instruction uses and
// initialises the param pointer. You *must* before the
// first use of param.
// use_params(n) Claim n extra bytes of param space beyond what was
// claimed using delcare_param.
// param A const byte pointer for the parameter space claimed by
// this instruction.
// binop(op) Implement a binary operation on the stack using the
// specified C++ operator.
// NOT_IMPLEMENTED Any instruction body containing this will exit the
// program with an assertion error. Instructions that are
// not implemented should also be marked NILOP in the
// opcodes tables this will cause the code class to spot
// them in a live code stream and throw a runtime_error
// instead.
// push(n) Push the value n onto the stack.
// pop() Pop the top most value and return it.
//
// You have access to the following named fast 'registers':
// sp = The pointer to the current top of stack, the last value
// pushed.
// seg = A reference to the Segment this code is running over.
// is = The current slot index
// isb = The original base slot index at the start of this rule
// isf = The first positioned slot
// isl = The last positioned slot
// ip = The current instruction pointer
// endPos = Position of advance of last cluster
// dir = writing system directionality of the font
// #define NOT_IMPLEMENTED assert(false)
// #define NOT_IMPLEMENTED
#define binop(op) const uint32_t a = pop(); *reg.sp = uint32_t(*reg.sp) op a
#define sbinop(op) const int32_t a = pop(); *reg.sp = int32_t(*reg.sp) op a
#define use_params(n) reg.dp += (n)
#define declare_params(n) const byte * param = reg.dp; \
use_params(n);
#define push(n) { *++reg.sp = n; }
#define pop() (*reg.sp--)
#define slotat(x) (reg.is[(x)])
#define DIE { reg.os=reg.seg.slots().end(); reg.status = Machine::died_early; EXIT(1); }
//#define next_slot(x, op) { op x; while(x -> deleted()) { op x;}; }
// TODO: Find out if Pass::runFSM can be made smarter when finishing building the map to avoid the need for this check.
// TODO: Move more common code into an opcodes_preamble.hxx, to avoid macros as functions.
#define position_context(slat) { \
if (!reg.positioned && (slat == gr_slatPosX || slat == gr_slatPosY)) \
{ \
auto last = reg.ctxt.map.back(); \
if (last != reg.seg.slots().end()) ++last; \
reg.seg.positionSlots(nullptr, reg.ctxt.map.front(), last, reg.seg.currdir()); \
reg.positioned = true; \
} \
}
STARTOP(nop)
do {} while (0);
ENDOP
STARTOP(push_byte)
declare_params(1);
push(int8_t(*param));
ENDOP
STARTOP(push_byte_u)
declare_params(1);
push(uint8_t(*param));
ENDOP
STARTOP(push_short)
declare_params(2);
const int16_t r = int16_t(param[0]) << 8
| uint8_t(param[1]);
push(r);
ENDOP
STARTOP(push_short_u)
declare_params(2);
const uint16_t r = uint16_t(param[0]) << 8
| uint8_t(param[1]);
push(r);
ENDOP
STARTOP(push_long)
declare_params(4);
const int32_t r = int32_t(param[0]) << 24
| uint32_t(param[1]) << 16
| uint32_t(param[2]) << 8
| uint8_t(param[3]);
push(r);
ENDOP
STARTOP(add)
binop(+);
ENDOP
STARTOP(sub)
binop(-);
ENDOP
STARTOP(mul)
binop(*);
ENDOP
STARTOP(div_)
const int32_t b = pop();
const int32_t a = int32_t(*reg.sp);
if (b == 0 || (a == std::numeric_limits<int32_t>::min() && b == -1)) DIE;
*reg.sp = int32_t(*reg.sp) / b;
ENDOP
STARTOP(min_)
const int32_t a = pop(), b = *reg.sp;
if (a < b) *reg.sp = a;
ENDOP
STARTOP(max_)
const int32_t a = pop(), b = *reg.sp;
if (a > b) *reg.sp = a;
ENDOP
STARTOP(neg)
*reg.sp = uint32_t(-int32_t(*reg.sp));
ENDOP
STARTOP(trunc8)
*reg.sp = uint8_t(*reg.sp);
ENDOP
STARTOP(trunc16)
*reg.sp = uint16_t(*reg.sp);
ENDOP
STARTOP(cond)
const uint32_t f = pop(), t = pop(), c = pop();
push(c ? t : f);
ENDOP
STARTOP(and_)
binop(&&);
ENDOP
STARTOP(or_)
binop(||);
ENDOP
STARTOP(not_)
*reg.sp = !*reg.sp;
ENDOP
STARTOP(equal)
binop(==);
ENDOP
STARTOP(not_eq_)
binop(!=);
ENDOP
STARTOP(less)
sbinop(<);
ENDOP
STARTOP(gtr)
sbinop(>);
ENDOP
STARTOP(less_eq)
sbinop(<=);
ENDOP
STARTOP(gtr_eq)
sbinop(>=);
ENDOP
STARTOP(next)
if (reg.is - &reg.ctxt.map[0] >= int(reg.ctxt.map.size())) DIE
if (reg.os != reg.seg.slots().end())
{
if (reg.os == reg.ctxt.highwater())
reg.ctxt.highpassed(true);
++reg.os;
}
++reg.is;
ENDOP
//STARTOP(next_n)
// use_params(1);
// NOT_IMPLEMENTED;
//declare_params(1);
//const size_t num = uint8_t(*param);
//ENDOP
//STARTOP(copy_next)
// if (reg.os) next_slot(reg.os,++);
// next_slot(reg.is, ++);
// ENDOP
STARTOP(put_glyph_8bit_obs)
declare_params(1);
const unsigned int output_class = uint8_t(*param);
reg.os->glyph(reg.seg, reg.seg.getClassGlyph(output_class, 0));
ENDOP
STARTOP(put_subs_8bit_obs)
declare_params(3);
const int slot_ref = int8_t(param[0]);
const unsigned int input_class = uint8_t(param[1]),
output_class = uint8_t(param[2]);
uint16_t index;
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
index = reg.seg.findClassIndex(input_class, slot->gid());
reg.os->glyph(reg.seg, reg.seg.getClassGlyph(output_class, index));
}
ENDOP
STARTOP(put_copy)
declare_params(1);
const int slot_ref = int8_t(*param);
if (reg.os != reg.seg.slots().end() && !reg.os->deleted())
{
auto ref = slotat(slot_ref);
if (ref != reg.seg.slots().end() && ref != reg.os)
{
if (!reg.os->isBase() || reg.os->isParent()) DIE
auto g = reg.os->generation();
*reg.os = *ref;
reg.os->generation() = g;
if (!reg.os->isBase())
reg.os->attachedTo()->add_child(&*reg.os);
}
reg.os->copied(false);
reg.os->deleted(false);
}
ENDOP
STARTOP(insert)
if (reg.ctxt.decMax() <= 0) DIE;
auto iss = reg.os;
while (iss != reg.seg.slots().end() && iss->deleted()) ++iss;
auto slot = reg.seg.slots().emplace(iss++, reg.seg.numAttrs());
if (slot == reg.seg.slots().end()) DIE;
slot->generation() += reg.seg.slots().size();
slot->clusterhead(true);
switch ((slot == reg.seg.slots().begin()) << 1 | (iss == reg.seg.slots().end()))
{
case 0: // Medial insertion
slot->before(std::prev(slot)->after());
slot->original(iss->original());
slot->after(iss->before());
break;
case 1: // Final insertion
slot->before(std::prev(slot)->before());
slot->original(std::prev(slot)->original());
slot->after(std::prev(slot)->after());
break;
case 2: // Initial insertion
slot->before(iss->before());
slot->original(iss->original());
slot->after(iss->before());
break;
default: // Singleton insertion
slot->original(reg.seg.defaultOriginal());
break;
}
if (reg.os == reg.ctxt.highwater())
reg.ctxt.highpassed(false);
reg.os = slot;
reg.seg.extendLength(1);
for (auto i = reg.is; i != reg.ctxt.map.end(); ++i) ++*i;
if (reg.is >= reg.ctxt.map.begin())
--reg.is;
ENDOP
STARTOP(delete_)
if (reg.os == reg.seg.slots().end() || reg.os->deleted()) DIE
reg.seg.extendLength(-1);
reg.os->deleted(true);
if (reg.os == reg.ctxt.highwater())
reg.ctxt.highwater(std::next(reg.os));
if (!(*reg.is)->copied()) {
*reg.is = slotref::from(new Slot(std::move(*reg.os)));
(*reg.is)->copied(true);
}
else { **reg.is = std::move(*reg.os); }
reg.os = reg.seg.slots().erase(reg.os);
// if (reg.os != reg.seg.slots().begin())
--reg.os;
for (auto i = reg.is+1; i != reg.ctxt.map.end(); ++i) --*i;
ENDOP
STARTOP(assoc)
declare_params(1);
unsigned int num = uint8_t(*param);
const int8_t * assocs = reinterpret_cast<const int8_t *>(param+1);
use_params(num);
int max = -1;
int min = -1;
while (num-- > 0)
{
int sr = *assocs++;
slotref ts = slotat(sr);
if (ts != reg.seg.slots().end() && (min == -1 || ts->before() < min)) min = ts->before();
if (ts != reg.seg.slots().end() && ts->after() > max) max = ts->after();
}
if (min > -1) // implies max > -1
{
reg.os->before(min);
reg.os->after(max);
}
ENDOP
STARTOP(cntxt_item)
// It turns out this is a cunningly disguised condition forward jump.
declare_params(3);
const int is_arg = int8_t(param[0]);
const size_t iskip = uint8_t(param[1]),
dskip = uint8_t(param[2]);
if (reg.isb + is_arg != reg.is)
{
reg.ip += iskip;
reg.dp += dskip;
push(true);
}
ENDOP
STARTOP(attr_set)
declare_params(1);
auto const slat = Slot::attrCode(uint8_t(*param));
int const val = pop();
reg.os->setAttr(reg.seg, slat, 0, val, reg.ctxt);
ENDOP
STARTOP(attr_add)
declare_params(1);
auto const slat = Slot::attrCode(uint8_t(*param));
uint32_t const val = pop();
position_context(slat)
uint32_t res = uint32_t(reg.os->getAttr(reg.seg, slat, 0));
reg.os->setAttr(reg.seg, slat, 0, int32_t(val + res), reg.ctxt);
ENDOP
STARTOP(attr_sub)
declare_params(1);
auto const slat = Slot::attrCode(uint8_t(*param));
uint32_t const val = pop();
position_context(slat)
uint32_t res = uint32_t(reg.os->getAttr(reg.seg, slat, 0));
reg.os->setAttr(reg.seg, slat, 0, int32_t(res - val), reg.ctxt);
ENDOP
STARTOP(attr_set_slot)
declare_params(1);
auto const slat = Slot::attrCode(uint8_t(*param));
int const offset = int(reg.is - reg.ctxt.map.begin())*int(slat == gr_slatAttTo);
int const val = pop() + offset;
reg.os->setAttr(reg.seg, slat, offset, val, reg.ctxt);
ENDOP
STARTOP(iattr_set_slot)
declare_params(2);
auto const slat = Slot::attrCode(uint8_t(param[0]));
uint8_t const idx = uint8_t(param[1]);
int const val = int(pop() + (reg.is - reg.ctxt.map.begin())*int(slat == gr_slatAttTo));
reg.os->setAttr(reg.seg, slat, idx, val, reg.ctxt);
ENDOP
STARTOP(push_slot_attr)
declare_params(2);
auto const slat = Slot::attrCode(uint8_t(param[0]));
int const slot_ref = int8_t(param[1]);
position_context(slat)
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
int res = slot->getAttr(reg.seg, slat, 0);
push(res);
}
ENDOP
STARTOP(push_glyph_attr_obs)
declare_params(2);
unsigned int const glyph_attr = uint8_t(param[0]);
int const slot_ref = int8_t(param[1]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
push(int32_t(reg.seg.glyphAttr(slot->gid(), glyph_attr)));
ENDOP
STARTOP(push_glyph_metric)
declare_params(3);
const auto glyph_attr = metrics(param[0]);
const int slot_ref = int8_t(param[1]);
const signed int attr_level = uint8_t(param[2]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
push(reg.seg.getGlyphMetric(&*slot, glyph_attr, attr_level, reg.ctxt.dir));
ENDOP
STARTOP(push_feat)
declare_params(2);
const unsigned int feat = uint8_t(param[0]);
const int slot_ref = int8_t(param[1]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
uint8_t fid = reg.seg.charinfo(slot->original())->fid();
push(reg.seg.getFeature(fid, feat));
}
ENDOP
STARTOP(push_att_to_gattr_obs)
declare_params(2);
const unsigned int glyph_attr = uint8_t(param[0]);
const int slot_ref = int8_t(param[1]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
auto att = slot->attachedTo();
auto & ref = att ? *att : *slot;
push(int32_t(reg.seg.glyphAttr(ref.gid(), glyph_attr)));
}
ENDOP
STARTOP(push_att_to_glyph_metric)
declare_params(3);
const auto glyph_attr = metrics(param[0]);
const int slot_ref = int8_t(param[1]);
const signed int attr_level = uint8_t(param[2]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
auto parent = slot->attachedTo();
if (!parent) parent = &*slot;
push(int32_t(reg.seg.getGlyphMetric(parent, glyph_attr, attr_level, reg.ctxt.dir)));
}
ENDOP
STARTOP(push_islot_attr)
declare_params(3);
auto const slat = Slot::attrCode(uint8_t(param[0]));
int const slot_ref = int8_t(param[1]),
idx = uint8_t(param[2]);
position_context(slat)
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
int res = slot->getAttr(reg.seg, slat, idx);
push(res);
}
ENDOP
#if 0
STARTOP(push_iglyph_attr) // not implemented
NOT_IMPLEMENTED;
ENDOP
#endif
STARTOP(pop_ret)
const uint32_t ret = pop();
EXIT(ret);
ENDOP
STARTOP(ret_zero)
EXIT(0);
ENDOP
STARTOP(ret_true)
EXIT(1);
ENDOP
STARTOP(iattr_set)
declare_params(2);
auto const slat = Slot::attrCode(uint8_t(param[0]));
uint8_t const idx = uint8_t(param[1]);
int const val = pop();
reg.os->setAttr(reg.seg, slat, idx, val, reg.ctxt);
ENDOP
STARTOP(iattr_add)
declare_params(2);
auto const slat = Slot::attrCode(uint8_t(param[0]));
uint8_t const idx = uint8_t(param[1]);
uint32_t const val = pop();
position_context(slat)
uint32_t res = uint32_t(reg.os->getAttr(reg.seg, slat, idx));
reg.os->setAttr(reg.seg, slat, idx, int32_t(val + res), reg.ctxt);
ENDOP
STARTOP(iattr_sub)
declare_params(2);
auto const slat = Slot::attrCode(uint8_t(param[0]));
uint8_t const idx = uint8_t(param[1]);
uint32_t const val = pop();
position_context(slat)
uint32_t res = uint32_t(reg.os->getAttr(reg.seg, slat, idx));
reg.os->setAttr(reg.seg, slat, idx, int32_t(res - val), reg.ctxt);
ENDOP
STARTOP(push_proc_state)
use_params(1);
push(1);
ENDOP
STARTOP(push_version)
push(0x00030000);
ENDOP
STARTOP(put_subs)
declare_params(5);
const int slot_ref = int8_t(param[0]);
const unsigned int input_class = uint8_t(param[1]) << 8
| uint8_t(param[2]);
const unsigned int output_class = uint8_t(param[3]) << 8
| uint8_t(param[4]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
int index = reg.seg.findClassIndex(input_class, slot->gid());
reg.os->glyph(reg.seg, reg.seg.getClassGlyph(output_class, index));
}
ENDOP
#if 0
STARTOP(put_subs2) // not implemented
NOT_IMPLEMENTED;
ENDOP
STARTOP(put_subs3) // not implemented
NOT_IMPLEMENTED;
ENDOP
#endif
STARTOP(put_glyph)
declare_params(2);
const unsigned int output_class = uint8_t(param[0]) << 8
| uint8_t(param[1]);
reg.os->glyph(reg.seg, reg.seg.getClassGlyph(output_class, 0));
ENDOP
STARTOP(push_glyph_attr)
declare_params(3);
const unsigned int glyph_attr = uint8_t(param[0]) << 8
| uint8_t(param[1]);
const int slot_ref = int8_t(param[2]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
push(int32_t(reg.seg.glyphAttr(slot->gid(), glyph_attr)));
ENDOP
STARTOP(push_att_to_glyph_attr)
declare_params(3);
const unsigned int glyph_attr = uint8_t(param[0]) << 8
| uint8_t(param[1]);
const int slot_ref = int8_t(param[2]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
auto att = slot->attachedTo();
auto & ref = att ? *att : *slot;
push(int32_t(reg.seg.glyphAttr(ref.gid(), glyph_attr)));
}
ENDOP
STARTOP(temp_copy)
#if 0
reg.seg.slots().push_back(Slot());
auto slot = --reg.seg.slots().end();
int16_t *tempUserAttrs = slot->userAttrs();
memcpy(&reg.seg.slots().back(), &*reg.os, sizeof(Slot));
memcpy(tempUserAttrs, reg.os->userAttrs(), reg.seg.numAttrs() * sizeof(uint16_t));
slot->userAttrs(tempUserAttrs);
slot->copied(true);
reg.seg.slots().erase(slot);
*reg.is = slot;
#else
auto slot = reg.seg.newSlot();
if (slot == reg.seg.slots().end() || reg.os == reg.seg.slots().end()) DIE;
// copy slot reg.os into new slot
*slot = *reg.os;
slot->copied(true);
// TODO: remove this once we're using gr::list methods. This is the
// hack that, that enables the hack, that enables debug output.
// slot.prev(std::prev(reg.os));
// slot.next(std::next(reg.os));
*reg.is = slot;
#endif
ENDOP
STARTOP(band)
binop(&);
ENDOP
STARTOP(bor)
binop(|);
ENDOP
STARTOP(bnot)
*reg.sp = ~*reg.sp;
ENDOP
STARTOP(setbits)
declare_params(4);
const uint16_t m = uint16_t(param[0]) << 8
| uint8_t(param[1]);
const uint16_t v = uint16_t(param[2]) << 8
| uint8_t(param[3]);
*reg.sp = ((*reg.sp) & ~m) | v;
ENDOP
STARTOP(set_feat)
declare_params(2);
const unsigned int feat = uint8_t(param[0]);
const int slot_ref = int8_t(param[1]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
uint8_t fid = reg.seg.charinfo(slot->original())->fid();
reg.seg.setFeature(fid, feat, pop());
}
ENDOP