Commit e4938dd2 authored by Marius Wachtler's avatar Marius Wachtler

only lookup __[set/get/del]slice__ for slice AST nodes

before this change
C()[slice(1,2)] = "a"
would call into `__setslice__` because it got handled as
C()[1,2] = "a"

This commits adds cpythons assign_slice/apply_slice and add rewriter support for them.
parent 2edfc49b
......@@ -287,24 +287,29 @@ void RewriterVar::addGuard(uint64_t val) {
rewriter->addAction([=]() { rewriter->_addGuard(this, val_var); }, { this, val_var }, ActionType::GUARD);
}
void Rewriter::_nextSlotJump(bool condition_eq) {
void Rewriter::_nextSlotJump(assembler::ConditionCode condition) {
// If a jump offset is larger then 0x80 the instruction encoding requires 6bytes instead of 2bytes.
// This adds up quickly, thats why we will try to find another jump to the slowpath with the same condition with a
// smaller offset and jump to it / use it as a trampoline.
// The benchmark show that this increases the performance slightly even though it introduces additional jumps.
int& last_jmp_offset = condition_eq ? offset_eq_jmp_next_slot : offset_ne_jmp_next_slot;
auto condition = condition_eq ? assembler::COND_EQUAL : assembler::COND_NOT_EQUAL;
int last_jmp_offset = -1;
for (auto it = next_slot_jmps.rbegin(), it_end = next_slot_jmps.rend(); it != it_end; ++it) {
if (std::get<2>(*it) == condition) {
last_jmp_offset = std::get<0>(*it);
break;
}
}
if (last_jmp_offset != -1 && assembler->bytesWritten() - last_jmp_offset < 0x80) {
assembler->jmp_cond(assembler::JumpDestination::fromStart(last_jmp_offset), condition);
} else {
last_jmp_offset = assembler->bytesWritten();
int last_jmp_offset = assembler->bytesWritten();
assembler->jmp_cond(assembler::JumpDestination::fromStart(rewrite->getSlotSize()), condition);
next_slot_jmps.emplace_back(last_jmp_offset, assembler->bytesWritten(), condition);
}
}
void Rewriter::_addGuard(RewriterVar* var, RewriterVar* val_constant) {
void Rewriter::_addGuard(RewriterVar* var, RewriterVar* val_constant, bool negate) {
if (LOG_IC_ASSEMBLY)
assembler->comment("_addGuard");
......@@ -316,12 +321,15 @@ void Rewriter::_addGuard(RewriterVar* var, RewriterVar* val_constant) {
assembler::Register reg = val_constant->getInReg(Location::any(), true, /* otherThan */ var_reg);
assembler->cmp(var_reg, reg);
} else {
assembler->cmp(var_reg, assembler::Immediate(val));
if (val == 0)
assembler->test(var_reg, var_reg);
else
assembler->cmp(var_reg, assembler::Immediate(val));
}
restoreArgs(); // can only do movs, doesn't affect flags, so it's safe
assertArgsInPlace();
_nextSlotJump(false /*= not equal jmp */);
_nextSlotJump(negate ? assembler::COND_EQUAL : assembler::COND_NOT_EQUAL);
var->bumpUse();
val_constant->bumpUse();
......@@ -333,32 +341,24 @@ void RewriterVar::addGuardNotEq(uint64_t val) {
STAT_TIMER(t0, "us_timer_rewriter", 10);
RewriterVar* val_var = rewriter->loadConst(val);
rewriter->addAction([=]() { rewriter->_addGuardNotEq(this, val_var); }, { this, val_var }, ActionType::GUARD);
rewriter->addAction([=]() { rewriter->_addGuard(this, val_var, true /* negate */); }, { this, val_var },
ActionType::GUARD);
}
void Rewriter::_addGuardNotEq(RewriterVar* var, RewriterVar* val_constant) {
if (LOG_IC_ASSEMBLY)
assembler->comment("_addGuardNotEq");
void RewriterVar::addGuardNotLt0() {
rewriter->addAction([=]() {
assembler::Register var_reg = this->getInReg();
rewriter->assembler->test(var_reg, var_reg);
assert(val_constant->is_constant);
uint64_t val = val_constant->constant_value;
rewriter->restoreArgs(); // can only do movs, doesn't affect flags, so it's safe
rewriter->assertArgsInPlace();
assembler::Register var_reg = var->getInReg();
if (isLargeConstant(val)) {
assembler::Register reg = val_constant->getInReg(Location::any(), true, /* otherThan */ var_reg);
assembler->cmp(var_reg, reg);
} else {
assembler->cmp(var_reg, assembler::Immediate(val));
}
restoreArgs(); // can only do movs, doesn't affect flags, so it's safe
assertArgsInPlace();
_nextSlotJump(true /*= equal jmp */);
rewriter->_nextSlotJump(assembler::COND_SIGN);
var->bumpUse();
val_constant->bumpUse();
bumpUse();
rewriter->assertConsistent();
assertConsistent();
}, { this }, ActionType::GUARD);
}
void RewriterVar::addAttrGuard(int offset, uint64_t val, bool negate) {
......@@ -405,7 +405,7 @@ void Rewriter::_addAttrGuard(RewriterVar* var, int offset, RewriterVar* val_cons
restoreArgs(); // can only do movs, doesn't affect flags, so it's safe
assertArgsInPlace();
_nextSlotJump(negate);
_nextSlotJump(negate ? assembler::COND_EQUAL : assembler::COND_NOT_EQUAL);
var->bumpUse();
val_constant->bumpUse();
......@@ -2220,8 +2220,6 @@ Rewriter::Rewriter(std::unique_ptr<ICSlotRewrite> rewrite, int num_args, const L
marked_inside_ic(false),
done_guarding(false),
last_guard_action(-1),
offset_eq_jmp_next_slot(-1),
offset_ne_jmp_next_slot(-1),
allocatable_regs(std_allocatable_regs) {
initPhaseCollecting();
......
......@@ -147,6 +147,7 @@ public:
void addGuard(uint64_t val);
void addGuardNotEq(uint64_t val);
void addGuardNotLt0();
void addAttrGuard(int offset, uint64_t val, bool negate = false);
RewriterVar* getAttr(int offset, Location loc = Location::any(), assembler::MovType type = assembler::MovType::Q);
// getAttrFloat casts to double (maybe I should make that separate?)
......@@ -502,8 +503,6 @@ protected:
}
int last_guard_action;
int offset_eq_jmp_next_slot;
int offset_ne_jmp_next_slot;
// keeps track of all jumps to the next slot so we can patch them if the size of the current slot changes
std::vector<NextSlotJumpInfo> next_slot_jmps;
......@@ -540,7 +539,7 @@ protected:
bool finishAssembly(int continue_offset, bool& should_fill_with_nops, bool& variable_size_slots) override;
void _nextSlotJump(bool condition_eq);
void _nextSlotJump(assembler::ConditionCode condition);
void _trap();
void _loadConst(RewriterVar* result, int64_t val);
void _setupCall(bool has_side_effects, llvm::ArrayRef<RewriterVar*> args = {},
......@@ -557,8 +556,7 @@ protected:
void _checkAndThrowCAPIException(RewriterVar* r, int64_t exc_val, assembler::MovType size);
// The public versions of these are in RewriterVar
void _addGuard(RewriterVar* var, RewriterVar* val_constant);
void _addGuardNotEq(RewriterVar* var, RewriterVar* val_constant);
void _addGuard(RewriterVar* var, RewriterVar* val_constant, bool negate = false);
void _addAttrGuard(RewriterVar* var, int offset, RewriterVar* val_constant, bool negate = false);
void _getAttr(RewriterVar* result, RewriterVar* var, int offset, Location loc = Location::any(),
assembler::MovType type = assembler::MovType::Q);
......
......@@ -1242,7 +1242,7 @@ static int slot_tp_descr_set(PyObject* self, PyObject* target, PyObject* value)
PyObject* slot_sq_item(PyObject* self, Py_ssize_t i) noexcept {
STAT_TIMER(t0, "us_timer_slot_sqitem", SLOT_AVOIDABILITY(self));
return getitemInternal<CAPI>(self, autoDecref(boxInt(i)));
return PyObject_GetItem(self, autoDecref(boxInt(i)));
}
/* Pyston change: static */ Py_ssize_t slot_sq_length(PyObject* self) noexcept {
......
......@@ -554,12 +554,27 @@ void ASTInterpreter::doStore(AST_expr* node, STOLEN(Value) value) {
Value target = visit_expr(subscript->value);
AUTO_DECREF(target.o);
Value slice = visit_slice(subscript->slice);
AUTO_DECREF(slice.o);
bool is_slice = (subscript->slice->type == AST_TYPE::Slice) && (((AST_Slice*)subscript->slice)->step == NULL);
if (is_slice) {
AST_Slice* slice = (AST_Slice*)subscript->slice;
Value lower = slice->lower ? visit_expr(slice->lower) : Value();
AUTO_XDECREF(lower.o);
Value upper = slice->upper ? visit_expr(slice->upper) : Value();
AUTO_XDECREF(upper.o);
assert(slice->step == NULL);
if (jit)
jit->emitSetItem(target, slice, value);
setitem(target.o, slice.o, value.o);
if (jit)
jit->emitAssignSlice(target, lower, upper, value);
assignSlice(target.o, lower.o, upper.o, value.o);
} else {
Value slice = visit_slice(subscript->slice);
AUTO_DECREF(slice.o);
if (jit)
jit->emitSetItem(target, slice, value);
setitem(target.o, slice.o, value.o);
}
} else {
RELEASE_ASSERT(0, "not implemented");
}
......@@ -1321,11 +1336,26 @@ Value ASTInterpreter::visit_delete(AST_Delete* node) {
AST_Subscript* sub = (AST_Subscript*)target_;
Value value = visit_expr(sub->value);
AUTO_DECREF(value.o);
Value slice = visit_slice(sub->slice);
AUTO_DECREF(slice.o);
if (jit)
jit->emitDelItem(value, slice);
delitem(value.o, slice.o);
bool is_slice = (sub->slice->type == AST_TYPE::Slice) && (((AST_Slice*)sub->slice)->step == NULL);
if (is_slice) {
AST_Slice* slice = (AST_Slice*)sub->slice;
Value lower = slice->lower ? visit_expr(slice->lower) : Value();
AUTO_XDECREF(lower.o);
Value upper = slice->upper ? visit_expr(slice->upper) : Value();
AUTO_XDECREF(upper.o);
assert(slice->step == NULL);
if (jit)
jit->emitAssignSlice(value, lower, upper, jit->imm(0ul));
assignSlice(value.o, lower.o, upper.o, NULL);
} else {
Value slice = visit_slice(sub->slice);
AUTO_DECREF(slice.o);
if (jit)
jit->emitDelItem(value, slice);
delitem(value.o, slice.o);
}
break;
}
case AST_TYPE::Attribute: {
......@@ -1735,10 +1765,31 @@ Value ASTInterpreter::visit_name(AST_Name* node) {
Value ASTInterpreter::visit_subscript(AST_Subscript* node) {
Value value = visit_expr(node->value);
AUTO_DECREF(value.o);
Value slice = visit_slice(node->slice);
AUTO_DECREF(slice.o);
return Value(getitem(value.o, slice.o), jit ? jit->emitGetItem(node, value, slice) : NULL);
bool is_slice = (node->slice->type == AST_TYPE::Slice) && (((AST_Slice*)node->slice)->step == NULL);
if (is_slice) {
AST_Slice* slice = (AST_Slice*)node->slice;
Value lower = slice->lower ? visit_expr(slice->lower) : Value();
AUTO_XDECREF(lower.o);
Value upper = slice->upper ? visit_expr(slice->upper) : Value();
AUTO_XDECREF(upper.o);
assert(slice->step == NULL);
Value v;
if (jit)
v.var = jit->emitApplySlice(value, lower, upper);
v.o = applySlice(value.o, lower.o, upper.o);
return v;
} else {
Value slice = visit_slice(node->slice);
AUTO_DECREF(slice.o);
Value v;
if (jit)
v.var = jit->emitGetItem(node, value, slice);
v.o = getitem(value.o, slice.o);
return v;
}
}
Value ASTInterpreter::visit_list(AST_List* node) {
......
......@@ -208,6 +208,14 @@ RewriterVar* JitFragmentWriter::emitAugbinop(AST_expr* node, RewriterVar* lhs, R
return emitPPCall((void*)augbinop, { lhs, rhs, imm(op_type) }, 2 * 320, node).first->setType(RefType::OWNED);
}
RewriterVar* JitFragmentWriter::emitApplySlice(RewriterVar* target, RewriterVar* lower, RewriterVar* upper) {
if (!lower)
lower = imm(0ul);
if (!upper)
upper = imm(0ul);
return emitPPCall((void*)applySlice, { target, lower, upper }, 256).first->setType(RefType::OWNED);
}
RewriterVar* JitFragmentWriter::emitBinop(AST_expr* node, RewriterVar* lhs, RewriterVar* rhs, int op_type) {
return emitPPCall((void*)binop, { lhs, rhs, imm(op_type) }, 2 * 240, node).first->setType(RefType::OWNED);
}
......@@ -553,6 +561,15 @@ RewriterVar* JitFragmentWriter::emitYield(RewriterVar* v) {
return rtn;
}
void JitFragmentWriter::emitAssignSlice(RewriterVar* target, RewriterVar* lower, RewriterVar* upper,
RewriterVar* value) {
if (!lower)
lower = imm(0ul);
if (!upper)
upper = imm(0ul);
emitPPCall((void*)assignSlice, { target, lower, upper, value }, 256).first;
}
void JitFragmentWriter::emitDelAttr(RewriterVar* target, BoxedString* attr) {
emitPPCall((void*)delattr, { target, imm(attr) }, 144).first;
}
......
......@@ -239,6 +239,7 @@ public:
RewriterVar* imm(void* val);
RewriterVar* emitAugbinop(AST_expr* node, RewriterVar* lhs, RewriterVar* rhs, int op_type);
RewriterVar* emitApplySlice(RewriterVar* target, RewriterVar* lower, RewriterVar* upper);
RewriterVar* emitBinop(AST_expr* node, RewriterVar* lhs, RewriterVar* rhs, int op_type);
RewriterVar* emitCallattr(AST_expr* node, RewriterVar* obj, BoxedString* attr, CallattrFlags flags,
const llvm::ArrayRef<RewriterVar*> args, std::vector<BoxedString*>* keyword_names);
......@@ -275,6 +276,7 @@ public:
std::vector<RewriterVar*> emitUnpackIntoArray(RewriterVar* v, uint64_t num);
RewriterVar* emitYield(RewriterVar* v);
void emitAssignSlice(RewriterVar* target, RewriterVar* lower, RewriterVar* upper, RewriterVar* value);
void emitDelAttr(RewriterVar* target, BoxedString* attr);
void emitDelGlobal(BoxedString* name);
void emitDelItem(RewriterVar* target, RewriterVar* slice);
......
......@@ -331,6 +331,9 @@ public:
CompilerVariable* getitem(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var,
CompilerVariable* slice) override {
ExceptionStyle target_exception_style = info.preferredExceptionStyle();
bool do_patchpoint = ENABLE_ICGETITEMS;
if (slice->getType() == UNBOXED_SLICE) {
UnboxedSlice slice_val = extractSlice(slice);
......@@ -340,31 +343,37 @@ public:
= var->getType()->getattrType(attr, true)->callType(ArgPassSpec(1), { SLICE }, NULL);
assert(return_type->getConcreteType() == return_type);
if (return_type != UNDEF) {
llvm::Value* cstart, *cstop;
cstart = slice_val.start ? slice_val.start->makeConverted(emitter, UNKNOWN)->getValue()
: emitter.setType(getNullPtr(g.llvm_value_type_ptr), RefType::BORROWED);
cstop = slice_val.stop ? slice_val.stop->makeConverted(emitter, UNKNOWN)->getValue()
: emitter.setType(getNullPtr(g.llvm_value_type_ptr), RefType::BORROWED);
llvm::Value* r = emitter.createCall3(info.unw_info, g.funcs.apply_slice, var->getValue(), cstart,
cstop, CAPI, getNullPtr(g.llvm_value_type_ptr));
emitter.setType(r, RefType::OWNED);
llvm::Value* cstart, *cstop;
cstart = slice_val.start ? slice_val.start->makeConverted(emitter, UNKNOWN)->getValue()
: emitter.setType(getNullPtr(g.llvm_value_type_ptr), RefType::BORROWED);
cstop = slice_val.stop ? slice_val.stop->makeConverted(emitter, UNKNOWN)->getValue()
: emitter.setType(getNullPtr(g.llvm_value_type_ptr), RefType::BORROWED);
llvm::Value* r = NULL;
if (do_patchpoint) {
ICSetupInfo* pp = createGetitemIC(info.getTypeRecorder(), info.getBJitICInfo());
llvm::Instruction* uncasted = emitter.createIC(
pp, (void*)(target_exception_style == CAPI ? pyston::apply_slice : pyston::applySlice),
{ var->getValue(), cstart, cstop }, info.unw_info, target_exception_style,
getNullPtr(g.llvm_value_type_ptr));
r = createAfter<llvm::IntToPtrInst>(uncasted, uncasted, g.llvm_value_type_ptr, "");
} else {
r = emitter.createCall3(
info.unw_info, target_exception_style == CAPI ? g.funcs.apply_slice : g.funcs.applySlice,
var->getValue(), cstart, cstop, target_exception_style, getNullPtr(g.llvm_value_type_ptr));
}
emitter.setType(r, RefType::OWNED);
if (target_exception_style == CAPI)
emitter.setNullable(r, true);
if (return_type != UNDEF)
return new ConcreteCompilerVariable(static_cast<ConcreteCompilerType*>(return_type), r);
} else {
// TODO: we could directly emit an exception if we know getitem is undefined but for now let it just
// call the normal getitem which will raise the exception
}
return new ConcreteCompilerVariable(UNKNOWN, r);
}
}
ConcreteCompilerVariable* converted_slice = slice->makeConverted(emitter, slice->getBoxType());
ExceptionStyle target_exception_style = info.preferredExceptionStyle();
bool do_patchpoint = ENABLE_ICGETITEMS;
llvm::Value* rtn;
if (do_patchpoint) {
ICSetupInfo* pp = createGetitemIC(info.getTypeRecorder(), info.getBJitICInfo());
......
......@@ -1508,7 +1508,6 @@ private:
CompilerVariable* evalSubscript(AST_Subscript* node, const UnwindInfo& unw_info) {
CompilerVariable* value = evalExpr(node->value, unw_info);
CompilerVariable* slice = evalSlice(node->slice, unw_info);
CompilerVariable* rtn = value->getitem(emitter, getOpInfoForNode(node, unw_info), slice);
return rtn;
}
......@@ -1992,31 +1991,59 @@ private:
t->setattr(emitter, getEmptyOpInfo(unw_info), target->attr.getBox(), val);
}
void _doSetitem(AST_Subscript* target, CompilerVariable* val, const UnwindInfo& unw_info) {
CompilerVariable* tget = evalExpr(target->value, unw_info);
CompilerVariable* slice = evalSlice(target->slice, unw_info);
ConcreteCompilerVariable* converted_target = tget->makeConverted(emitter, tget->getBoxType());
ConcreteCompilerVariable* converted_slice = slice->makeConverted(emitter, slice->getBoxType());
ConcreteCompilerVariable* converted_val = val->makeConverted(emitter, val->getBoxType());
void _assignSlice(llvm::Value* target, llvm::Value* value, const UnboxedSlice& slice_val,
const UnwindInfo& unw_info) {
llvm::Value* cstart, *cstop;
cstart = slice_val.start ? slice_val.start->makeConverted(emitter, UNKNOWN)->getValue()
: emitter.setType(getNullPtr(g.llvm_value_type_ptr), RefType::BORROWED);
cstop = slice_val.stop ? slice_val.stop->makeConverted(emitter, UNKNOWN)->getValue()
: emitter.setType(getNullPtr(g.llvm_value_type_ptr), RefType::BORROWED);
// TODO add a CompilerVariable::setattr, which can (similar to getitem)
// statically-resolve the function if possible, and only fall back to
// patchpoints if it couldn't.
bool do_patchpoint = ENABLE_ICSETITEMS;
if (do_patchpoint) {
ICSetupInfo* pp = createSetitemIC(getEmptyOpInfo(unw_info).getTypeRecorder());
std::vector<llvm::Value*> llvm_args;
llvm_args.push_back(converted_target->getValue());
llvm_args.push_back(converted_slice->getValue());
llvm_args.push_back(converted_val->getValue());
llvm_args.push_back(target);
llvm_args.push_back(cstart);
llvm_args.push_back(cstop);
llvm_args.push_back(value);
emitter.createIC(pp, (void*)pyston::setitem, llvm_args, unw_info);
emitter.createIC(pp, (void*)pyston::assignSlice, llvm_args, unw_info);
} else {
emitter.createCall3(unw_info, g.funcs.setitem, converted_target->getValue(), converted_slice->getValue(),
converted_val->getValue());
emitter.createCall(unw_info, g.funcs.assignSlice, { target, cstart, cstop, value });
}
}
void _doSetitem(AST_Subscript* target, CompilerVariable* val, const UnwindInfo& unw_info) {
CompilerVariable* tget = evalExpr(target->value, unw_info);
CompilerVariable* slice = evalSlice(target->slice, unw_info);
ConcreteCompilerVariable* converted_target = tget->makeConverted(emitter, tget->getBoxType());
ConcreteCompilerVariable* converted_val = val->makeConverted(emitter, val->getBoxType());
if (slice->getType() == UNBOXED_SLICE && !extractSlice(slice).step) {
_assignSlice(converted_target->getValue(), converted_val->getValue(), extractSlice(slice), unw_info);
} else {
ConcreteCompilerVariable* converted_slice = slice->makeConverted(emitter, slice->getBoxType());
// TODO add a CompilerVariable::setattr, which can (similar to getitem)
// statically-resolve the function if possible, and only fall back to
// patchpoints if it couldn't.
bool do_patchpoint = ENABLE_ICSETITEMS;
if (do_patchpoint) {
ICSetupInfo* pp = createSetitemIC(getEmptyOpInfo(unw_info).getTypeRecorder());
std::vector<llvm::Value*> llvm_args;
llvm_args.push_back(converted_target->getValue());
llvm_args.push_back(converted_slice->getValue());
llvm_args.push_back(converted_val->getValue());
emitter.createIC(pp, (void*)pyston::setitem, llvm_args, unw_info);
} else {
emitter.createCall3(unw_info, g.funcs.setitem, converted_target->getValue(),
converted_slice->getValue(), converted_val->getValue());
}
}
}
......@@ -2125,19 +2152,27 @@ private:
CompilerVariable* slice = evalSlice(target->slice, unw_info);
ConcreteCompilerVariable* converted_target = tget->makeConverted(emitter, tget->getBoxType());
ConcreteCompilerVariable* converted_slice = slice->makeConverted(emitter, slice->getBoxType());
bool do_patchpoint = ENABLE_ICDELITEMS;
if (do_patchpoint) {
ICSetupInfo* pp = createDelitemIC(getEmptyOpInfo(unw_info).getTypeRecorder());
if (slice->getType() == UNBOXED_SLICE && !extractSlice(slice).step) {
_assignSlice(converted_target->getValue(),
emitter.setType(getNullPtr(g.llvm_value_type_ptr), RefType::BORROWED), extractSlice(slice),
unw_info);
} else {
ConcreteCompilerVariable* converted_slice = slice->makeConverted(emitter, slice->getBoxType());
std::vector<llvm::Value*> llvm_args;
llvm_args.push_back(converted_target->getValue());
llvm_args.push_back(converted_slice->getValue());
bool do_patchpoint = ENABLE_ICDELITEMS;
if (do_patchpoint) {
ICSetupInfo* pp = createDelitemIC(getEmptyOpInfo(unw_info).getTypeRecorder());
emitter.createIC(pp, (void*)pyston::delitem, llvm_args, unw_info);
} else {
emitter.createCall2(unw_info, g.funcs.delitem, converted_target->getValue(), converted_slice->getValue());
std::vector<llvm::Value*> llvm_args;
llvm_args.push_back(converted_target->getValue());
llvm_args.push_back(converted_slice->getValue());
emitter.createIC(pp, (void*)pyston::delitem, llvm_args, unw_info);
} else {
emitter.createCall2(unw_info, g.funcs.delitem, converted_target->getValue(),
converted_slice->getValue());
}
}
}
......
......@@ -229,6 +229,8 @@ void initGlobalFuncs(GlobalState& g) {
GET(getiterHelper);
GET(hasnext);
GET(apply_slice);
GET(applySlice);
GET(assignSlice);
GET(unpackIntoArray);
GET(raiseAttributeError);
......
......@@ -39,7 +39,7 @@ struct GlobalFuncs {
llvm::Value* getattr, *getattr_capi, *setattr, *delattr, *delitem, *delGlobal, *nonzero, *binop, *compare,
*augbinop, *unboxedLen, *getitem, *getitem_capi, *getclsattr, *getGlobal, *setitem, *unaryop, *import,
*importFrom, *importStar, *repr, *exceptionMatches, *yield_capi, *getiterHelper, *hasnext, *setGlobal,
*apply_slice;
*apply_slice, *applySlice, *assignSlice;
llvm::Value* unpackIntoArray, *raiseAttributeError, *raiseAttributeErrorStr, *raiseAttributeErrorCapi,
*raiseAttributeErrorStrCapi, *raiseNotIterableError, *raiseIndexErrorStr, *raiseIndexErrorStrCapi,
......
......@@ -777,6 +777,15 @@ Box* instanceGetslice(Box* _inst, Box* i, Box* j) {
return runtimeCall(getslice_func, ArgPassSpec(2), i, j, NULL, NULL, NULL);
}
Box* instance_slice(PyObject* self, Py_ssize_t i, Py_ssize_t j) noexcept {
try {
return instanceGetslice(self, autoDecref(boxInt(i)), autoDecref((boxInt(j))));
} catch (ExcInfo e) {
setCAPIException(e);
return 0;
}
}
Box* instanceSetslice(Box* _inst, Box* i, Box* j, Box** sequence) {
RELEASE_ASSERT(_inst->cls == instance_cls, "");
BoxedInstance* inst = static_cast<BoxedInstance*>(_inst);
......@@ -2005,5 +2014,6 @@ void setupClassobj() {
instance_cls->tp_as_number->nb_index = instance_index;
instance_cls->tp_as_number->nb_power = instance_pow;
instance_cls->tp_as_number->nb_inplace_power = instance_ipow;
instance_cls->tp_as_sequence->sq_slice = instance_slice;
}
}
......@@ -377,13 +377,13 @@ extern "C" BORROWED(PyObject*) PyDict_GetItem(PyObject* dict, PyObject* key) noe
/* preserve the existing exception */
PyObject* err_type, *err_value, *err_tb;
PyErr_Fetch(&err_type, &err_value, &err_tb);
Box* b = getitemInternal<CAPI>(dict, key);
Box* b = PyObject_GetItem(dict, key);
/* ignore errors */
PyErr_Restore(err_type, err_value, err_tb);
Py_XDECREF(b);
return b;
} else {
Box* b = getitemInternal<CAPI>(dict, key);
Box* b = PyObject_GetItem(dict, key);
if (b == NULL)
PyErr_Clear();
else
......@@ -514,13 +514,7 @@ extern "C" int PyDict_DelItem(PyObject* op, PyObject* key) noexcept {
}
ASSERT(op->cls == attrwrapper_cls, "%s", getTypeName(op));
try {
delitem(op, key);
return 0;
} catch (ExcInfo e) {
setCAPIException(e);
return -1;
}
return PyObject_DelItem(op, key);
}
extern "C" int PyDict_DelItemString(PyObject* v, const char* key) noexcept {
......
......@@ -105,6 +105,8 @@ void force() {
FORCE(getiterHelper);
FORCE(hasnext);
FORCE(apply_slice);
FORCE(applySlice);
FORCE(assignSlice);
FORCE(unpackIntoArray);
FORCE(raiseAttributeError);
......
......@@ -6073,13 +6073,8 @@ static Box* callItemAttr(Box* target, BoxedString* item_str, Box* item, Box* val
if (rewrite_args) {
CallattrRewriteArgs crewrite_args(rewrite_args);
Box* r;
if (value)
r = callattrInternal2<S, REWRITABLE>(target, item_str, CLASS_ONLY, &crewrite_args, ArgPassSpec(2), item,
value);
else
r = callattrInternal1<S, REWRITABLE>(target, item_str, CLASS_ONLY, &crewrite_args, ArgPassSpec(1), item);
Box* r = callattrInternal2<S, REWRITABLE>(target, item_str, CLASS_ONLY, &crewrite_args,
ArgPassSpec(value ? 2 : 1), item, value);
if (crewrite_args.isSuccessful()) {
rewrite_args->out_success = true;
if (r || PyErr_Occurred())
......@@ -6090,173 +6085,288 @@ static Box* callItemAttr(Box* target, BoxedString* item_str, Box* item, Box* val
}
return r;
} else {
if (value)
return callattrInternal2<S, NOT_REWRITABLE>(target, item_str, CLASS_ONLY, NULL, ArgPassSpec(2), item,
value);
else
return callattrInternal1<S, NOT_REWRITABLE>(target, item_str, CLASS_ONLY, NULL, ArgPassSpec(1), item);
return callattrInternal2<S, NOT_REWRITABLE>(target, item_str, CLASS_ONLY, NULL, ArgPassSpec(value ? 2 : 1),
item, value);
}
}
#define ISINDEX(x) ((x) == NULL || PyInt_Check(x) || PyLong_Check(x) || PyIndex_Check(x))
extern "C" PyObject* apply_slice(PyObject* u, PyObject* v, PyObject* w) noexcept /* return u[v:w] */
{
// TODO: add rewriting here
static int64_t isSliceIndexHelper(Box* a, Box* b) {
return ISINDEX(a) && ISINDEX(b);
}
PyTypeObject* tp = u->cls;
PySequenceMethods* sq = tp->tp_as_sequence;
template <ExceptionStyle S> static Box* applySliceHelper(Box* u, Box* a, Box* b) noexcept(S == CAPI) {
Py_ssize_t ilow = 0, ihigh = PY_SSIZE_T_MAX;
if (!_PyEval_SliceIndex(a, &ilow)) {
if (S == CXX)
throwCAPIException();
return NULL;
}
if (!_PyEval_SliceIndex(b, &ihigh)) {
if (S == CXX)
throwCAPIException();
return NULL;
}
if (sq && sq->sq_slice && ISINDEX(v) && ISINDEX(w)) {
Py_ssize_t ilow = 0, ihigh = PY_SSIZE_T_MAX;
if (!_PyEval_SliceIndex(v, &ilow))
return NULL;
if (!_PyEval_SliceIndex(w, &ihigh))
return NULL;
return PySequence_GetSlice(u, ilow, ihigh);
} else {
PyObject* slice = PySlice_New(v, w, NULL);
if (slice != NULL) {
PyObject* res = PyObject_GetItem(u, slice);
Py_DECREF(slice);
return res;
} else
return NULL;
PySequenceMethods* sq = u->cls->tp_as_sequence;
if (ilow < 0 || ihigh < 0) {
if (sq->sq_length) {
Py_ssize_t l = (*sq->sq_length)(u);
if (l < 0) {
if (S == CXX)
throwCAPIException();
return NULL;
}
if (ilow < 0)
ilow += l;
if (ihigh < 0)
ihigh += l;
}
}
Box* rtn = sq->sq_slice(u, ilow, ihigh);
if (S == CXX && !rtn)
throwCAPIException();
return rtn;
}
// This function decides whether to call the slice operator (e.g. __getslice__)
// or the item operator (__getitem__).
template <ExceptionStyle S, Rewritable rewritable>
static Box* callItemOrSliceAttr(Box* target, BoxedString* item_str, BoxedString* slice_str, Box* slice, Box* value,
CallRewriteArgs* rewrite_args) noexcept(S == CAPI) {
if (rewritable == NOT_REWRITABLE) {
assert(!rewrite_args);
rewrite_args = NULL;
template <ExceptionStyle S> static int assignSliceHelper(Box* u, Box* a, Box* b, Box* x) noexcept(S == CAPI) {
Py_ssize_t ilow = 0, ihigh = PY_SSIZE_T_MAX;
if (!_PyEval_SliceIndex(a, &ilow)) {
if (S == CXX)
throwCAPIException();
return -1;
}
if (!_PyEval_SliceIndex(b, &ihigh)) {
if (S == CXX)
throwCAPIException();
return -1;
}
// This function contains a lot of logic for deciding between whether to call
// the slice operator or the item operator, so we can match CPython's behavior
// on custom classes that define those operators. However, for builtin types,
// we know we can call either and the behavior will be the same. Adding all those
// guards are unnecessary and bad for performance.
//
// Also, for special slicing logic (e.g. open slice ranges [:]), the builtin types
// have C-implemented functions that already handle all the edge cases, so we don't
// need to have a slowpath for them here.
if (target->cls == list_cls || target->cls == str_cls || target->cls == unicode_cls) {
if (rewrite_args) {
rewrite_args->obj->addAttrGuard(offsetof(Box, cls), (uint64_t)target->cls);
PySequenceMethods* sq = u->cls->tp_as_sequence;
if (ilow < 0 || ihigh < 0) {
if (sq->sq_length) {
Py_ssize_t l = (*sq->sq_length)(u);
if (l < 0) {
if (S == CXX)
throwCAPIException();
return -1;
}
if (ilow < 0)
ilow += l;
if (ihigh < 0)
ihigh += l;
}
return callItemAttr<S, rewritable>(target, item_str, slice, value, rewrite_args);
}
// Guard on the type of the object (need to have the slice operator attribute to call it).
bool has_slice_attr = false;
if (rewrite_args) {
RewriterVar* target_cls = rewrite_args->obj->getAttr(offsetof(Box, cls));
GetattrRewriteArgs grewrite_args(rewrite_args->rewriter, target_cls, Location::any());
has_slice_attr = (bool)typeLookup(target->cls, slice_str, &grewrite_args);
if (!grewrite_args.isSuccessful()) {
rewrite_args = NULL;
} else {
RewriterVar* rtn;
ReturnConvention return_convention;
std::tie(rtn, return_convention) = grewrite_args.getReturn();
if (return_convention != ReturnConvention::HAS_RETURN && return_convention != ReturnConvention::NO_RETURN)
rewrite_args = NULL;
int rtn = sq->sq_ass_slice(u, ilow, ihigh, x);
if (S == CXX && rtn == -1)
throwCAPIException();
return rtn;
}
if (rewrite_args)
assert(has_slice_attr == (return_convention == ReturnConvention::HAS_RETURN));
}
static std::pair<RewriterVar*, RewriterVar*> extractIntSliceStartStop(std::unique_ptr<Rewriter>& rewriter,
PyObject* start, PyObject* stop,
RewriterVar* r_start, RewriterVar* r_stop) {
RewriterVar* r_start_int = NULL;
RewriterVar* r_stop_int = NULL;
if (start) {
r_start->addGuardNotEq(0);
r_start->addAttrGuard(offsetof(Box, cls), (uint64_t)int_cls);
r_start_int = r_start->getAttr(offsetof(BoxedInt, n));
r_start_int->addGuardNotLt0();
} else {
r_start->addGuard(0);
r_start_int = rewriter->loadConst(0);
}
if (stop) {
r_stop->addGuardNotEq(0);
r_stop->addAttrGuard(offsetof(Box, cls), (uint64_t)int_cls);
r_stop_int = r_stop->getAttr(offsetof(BoxedInt, n));
r_stop_int->addGuardNotLt0();
} else {
has_slice_attr = (bool)typeLookup(target->cls, slice_str);
r_stop->addGuard(0);
r_stop_int = rewriter->loadConst(PY_SSIZE_T_MAX);
}
return std::make_pair(r_start_int, r_stop_int);
}
if (!has_slice_attr) {
return callItemAttr<S, rewritable>(target, item_str, slice, value, rewrite_args);
}
/* return u[v:w] */
template <ExceptionStyle S>
PyObject* applySliceIntern(PyObject* u, PyObject* v, PyObject* w,
std::unique_ptr<Rewriter>& rewriter) noexcept(S == CAPI) {
RewriterVar* r_obj = NULL, * r_start = NULL, * r_stop = NULL, * r_cls = NULL, * r_sq = NULL;
if (rewriter) {
r_obj = rewriter->getArg(0);
r_start = rewriter->getArg(1);
r_stop = rewriter->getArg(2);
r_cls = r_obj->getAttr(offsetof(Box, cls));
r_sq = r_cls->getAttr(offsetof(BoxedClass, tp_as_sequence));
}
PySequenceMethods* sq = u->cls->tp_as_sequence;
if (sq && sq->sq_slice && isSliceIndexHelper(v, w)) {
// call __getslice__
if (rewriter) {
r_sq->addGuardNotEq(0);
r_sq->addAttrGuard(offsetof(PySequenceMethods, sq_slice), (intptr_t)sq->sq_slice);
// handle int >= 0 and null because it is so frequent
if ((!v || (v->cls == int_cls && (PyInt_AS_LONG(v) >= 0)))
&& (!w || (w->cls == int_cls && (PyInt_AS_LONG(w) >= 0)))) {
RewriterVar* r_start_int = NULL;
RewriterVar* r_stop_int = NULL;
std::tie(r_start_int, r_stop_int) = extractIntSliceStartStop(rewriter, v, w, r_start, r_stop);
RewriterVar* r_rtn = rewriter->call(true, (void*)sq->sq_slice, r_obj, r_start_int, r_stop_int)
->setType(RefType::OWNED);
if (S == CXX)
rewriter->checkAndThrowCAPIException(r_rtn);
rewriter->commitReturning(r_rtn);
} else {
RewriterVar* r_guard = rewriter->call(false, (void*)isSliceIndexHelper, r_start, r_stop);
r_guard->addGuardNotEq(0);
// Need a slice object to use the slice operators.
if (rewrite_args) {
rewrite_args->arg1->addAttrGuard(offsetof(Box, cls), (uint64_t)slice->cls);
}
if (slice->cls != slice_cls) {
return callItemAttr<S, rewritable>(target, item_str, slice, value, rewrite_args);
RewriterVar* r_rtn = rewriter->call(true, (void*)&applySliceHelper<S>, r_obj, r_start, r_stop)
->setType(RefType::OWNED);
rewriter->commitReturning(r_rtn);
}
}
return applySliceHelper<S>(u, v, w);
}
BoxedSlice* bslice = (BoxedSlice*)slice;
// call __getitem__
if (rewriter && (!sq || !sq->sq_slice)) {
// rewrite getitem
if (sq) {
r_sq->addGuardNotEq(0);
r_sq->addAttrGuard(offsetof(PySequenceMethods, sq_slice), 0);
} else
r_sq->addGuard(0);
// If we use slice notation with a step parameter (e.g. o[1:10:2]), the slice operator
// functions don't support that, so fallback to the item operator functions.
if (bslice->step->cls != none_cls) {
if (rewrite_args) {
rewrite_args->arg1->getAttr(offsetof(BoxedSlice, step))
->addAttrGuard(offsetof(Box, cls), (uint64_t)none_cls, /*negate=*/true);
}
RewriterVar* r_slice = rewriter->call(false, (void*)PySlice_New, r_start, r_stop, rewriter->loadConst(0ul))
->setType(RefType::OWNED);
Box* slice = PySlice_New(v, w, NULL);
AUTO_DECREF(slice);
return callItemAttr<S, rewritable>(target, item_str, slice, value, rewrite_args);
} else {
rewrite_args = NULL;
REWRITE_ABORTED("");
GetitemRewriteArgs rewrite_args(rewriter.get(), r_obj, r_slice, rewriter->getReturnDestination());
Box* rtn = getitemInternal<S>(u, slice, &rewrite_args);
// If the slice cannot be used as integer slices, also fall back to the get operator.
// We could optimize further here by having a version of isSliceIndex that
// creates guards, but it would only affect some rare edge cases.
if (!isSliceIndex(bslice->start) || !isSliceIndex(bslice->stop)) {
return callItemAttr<S, NOT_REWRITABLE>(target, item_str, slice, value, rewrite_args);
if (!rewrite_args.out_success) {
rewriter.reset(NULL);
} else if (rtn) {
rewriter->commitReturning(rewrite_args.out_rtn);
}
// If we don't specify the start/stop (e.g. o[:]), the slice operator functions
// CPython seems to use 0 and sys.maxint as the default values.
int64_t start = 0, stop = PyInt_GetMax();
if (S == CAPI) {
if (bslice->start != None)
if (!_PyEval_SliceIndex(bslice->start, &start))
return NULL;
if (bslice->stop != None)
if (!_PyEval_SliceIndex(bslice->stop, &stop))
return NULL;
return rtn;
} else {
PyObject* slice = PySlice_New(v, w, NULL);
if (slice != NULL) {
PyObject* res = PyObject_GetItem(u, slice);
Py_DECREF(slice);
if (S == CXX && !res)
throwCAPIException();
return res;
} else {
sliceIndex(bslice->start, &start);
sliceIndex(bslice->stop, &stop);
if (S == CXX)
throwCAPIException();
return NULL;
}
}
}
adjustNegativeIndicesOnObject(target, &start, &stop);
if (PyErr_Occurred())
throwCAPIException();
extern "C" PyObject* apply_slice(PyObject* u, PyObject* v, PyObject* w) noexcept {
std::unique_ptr<Rewriter> rewriter(
Rewriter::createRewriter(__builtin_extract_return_addr(__builtin_return_address(0)), 3, "apply_slice"));
Box* boxedStart = boxInt(start);
Box* boxedStop = boxInt(stop);
AUTO_DECREF(boxedStart);
AUTO_DECREF(boxedStop);
return applySliceIntern<CAPI>(u, v, w, rewriter);
}
if (rewrite_args) {
CallattrRewriteArgs crewrite_args(rewrite_args);
Box* r;
if (value)
r = callattrInternal3<S, REWRITABLE>(target, slice_str, CLASS_ONLY, &crewrite_args, ArgPassSpec(3),
boxedStart, boxedStop, value);
else
r = callattrInternal2<S, REWRITABLE>(target, slice_str, CLASS_ONLY, &crewrite_args, ArgPassSpec(2),
boxedStart, boxedStop);
extern "C" PyObject* applySlice(PyObject* u, PyObject* v, PyObject* w) {
std::unique_ptr<Rewriter> rewriter(
Rewriter::createRewriter(__builtin_extract_return_addr(__builtin_return_address(0)), 3, "apply_slice"));
if (crewrite_args.isSuccessful()) {
rewrite_args->out_success = true;
rewrite_args->out_rtn = crewrite_args.getReturn(ReturnConvention::HAS_RETURN);
return applySliceIntern<CXX>(u, v, w, rewriter);
}
/* u[v:w] = x */
template <ExceptionStyle S>
int assignSliceInternal(PyObject* u, PyObject* v, PyObject* w, PyObject* x,
std::unique_ptr<Rewriter>& rewriter) noexcept(S == CAPI) {
PySequenceMethods* sq = u->cls->tp_as_sequence;
if (sq && sq->sq_ass_slice && isSliceIndexHelper(v, w)) {
// call __setslice__ / __delslice__
if (rewriter) {
RewriterVar* r_obj = rewriter->getArg(0);
RewriterVar* r_start = rewriter->getArg(1);
RewriterVar* r_stop = rewriter->getArg(2);
RewriterVar* r_value = rewriter->getArg(3);
RewriterVar* r_cls = r_obj->getAttr(offsetof(Box, cls));
RewriterVar* r_sq = r_cls->getAttr(offsetof(BoxedClass, tp_as_sequence));
r_sq->addGuardNotEq(0);
r_sq->addAttrGuard(offsetof(PySequenceMethods, sq_ass_slice), (intptr_t)sq->sq_ass_slice);
// handle int >= 0 and null because it is so frequent
if ((!v || (v->cls == int_cls && (PyInt_AS_LONG(v) >= 0)))
&& (!w || (w->cls == int_cls && (PyInt_AS_LONG(w) >= 0)))) {
RewriterVar* r_start_int = NULL;
RewriterVar* r_stop_int = NULL;
std::tie(r_start_int, r_stop_int) = extractIntSliceStartStop(rewriter, v, w, r_start, r_stop);
RewriterVar* r_rtn
= rewriter->call(true, (void*)sq->sq_ass_slice, r_obj, r_start_int, r_stop_int, r_value);
if (S == CXX) {
rewriter->checkAndThrowCAPIException(r_rtn, -1, assembler::MovType::L);
rewriter->commit();
} else
rewriter->commitReturningNonPython(r_rtn);
} else {
RewriterVar* r_guard = rewriter->call(false, (void*)isSliceIndexHelper, r_start, r_stop);
r_guard->addGuardNotEq(0);
RewriterVar* r_rtn
= rewriter->call(true, (void*)&assignSliceHelper<S>, r_obj, r_start, r_stop, r_value);
if (S == CAPI)
rewriter->commitReturningNonPython(r_rtn);
else
rewriter->commit();
}
return r;
} else {
if (value)
return callattrInternal3<S, NOT_REWRITABLE>(target, slice_str, CLASS_ONLY, NULL, ArgPassSpec(3),
boxedStart, boxedStop, value);
else
return callattrInternal2<S, NOT_REWRITABLE>(target, slice_str, CLASS_ONLY, NULL, ArgPassSpec(2),
boxedStart, boxedStop);
}
return assignSliceHelper<S>(u, v, w, x);
}
// call __setitem__ / __delitem__
PyObject* slice = PySlice_New(v, w, NULL);
if (slice != NULL) {
int res;
if (x != NULL)
res = PyObject_SetItem(u, slice, x);
else
res = PyObject_DelItem(u, slice);
Py_DECREF(slice);
if (S == CXX && res == -1)
throwCAPIException();
return res;
} else {
if (S == CXX)
throwCAPIException();
return -1;
}
}
extern "C" int assign_slice(PyObject* u, PyObject* v, PyObject* w, PyObject* x) noexcept {
std::unique_ptr<Rewriter> rewriter(
Rewriter::createRewriter(__builtin_extract_return_addr(__builtin_return_address(0)), 4, "assign_slice"));
return assignSliceInternal<CAPI>(u, v, w, x, rewriter);
}
extern "C" void assignSlice(PyObject* u, PyObject* v, PyObject* w, PyObject* x) {
std::unique_ptr<Rewriter> rewriter(
Rewriter::createRewriter(__builtin_extract_return_addr(__builtin_return_address(0)), 4, "assign_slice"));
assignSliceInternal<CXX>(u, v, w, x, rewriter);
}
template <ExceptionStyle S, Rewritable rewritable>
Box* getitemInternal(Box* target, Box* slice, GetitemRewriteArgs* rewrite_args) noexcept(S == CAPI) {
if (rewritable == NOT_REWRITABLE) {
......@@ -6301,7 +6411,6 @@ Box* getitemInternal(Box* target, Box* slice, GetitemRewriteArgs* rewrite_args)
}
static BoxedString* getitem_str = getStaticString("__getitem__");
static BoxedString* getslice_str = getStaticString("__getslice__");
Box* rtn;
try {
......@@ -6309,7 +6418,7 @@ Box* getitemInternal(Box* target, Box* slice, GetitemRewriteArgs* rewrite_args)
CallRewriteArgs crewrite_args(rewrite_args->rewriter, rewrite_args->target, rewrite_args->destination);
crewrite_args.arg1 = rewrite_args->slice;
rtn = callItemOrSliceAttr<S, REWRITABLE>(target, getitem_str, getslice_str, slice, NULL, &crewrite_args);
rtn = callItemAttr<S, REWRITABLE>(target, getitem_str, slice, NULL, &crewrite_args);
if (!crewrite_args.out_success) {
rewrite_args = NULL;
......@@ -6317,7 +6426,7 @@ Box* getitemInternal(Box* target, Box* slice, GetitemRewriteArgs* rewrite_args)
rewrite_args->out_rtn = crewrite_args.out_rtn;
}
} else {
rtn = callItemOrSliceAttr<S, NOT_REWRITABLE>(target, getitem_str, getslice_str, slice, NULL, NULL);
rtn = callItemAttr<S, NOT_REWRITABLE>(target, getitem_str, slice, NULL, NULL);
}
} catch (ExcInfo e) {
if (S == CAPI) {
......@@ -6356,13 +6465,13 @@ Box* getitemInternal(Box* target, Box* slice, GetitemRewriteArgs* rewrite_args)
return rtn;
}
// Force instantiation of the template
template Box* getitemInternal<CAPI, REWRITABLE>(Box*, Box*, GetitemRewriteArgs*);
template Box* getitemInternal<CAPI, REWRITABLE>(Box*, Box*, GetitemRewriteArgs*) noexcept;
template Box* getitemInternal<CXX, REWRITABLE>(Box*, Box*, GetitemRewriteArgs*);
template Box* getitemInternal<CAPI, NOT_REWRITABLE>(Box*, Box*, GetitemRewriteArgs*);
template Box* getitemInternal<CAPI, NOT_REWRITABLE>(Box*, Box*, GetitemRewriteArgs*) noexcept;
template Box* getitemInternal<CXX, NOT_REWRITABLE>(Box*, Box*, GetitemRewriteArgs*);
// target[slice]
extern "C" Box* getitem(Box* target, Box* slice) {
template <ExceptionStyle S> static Box* getitemEntry(Box* target, Box* slice, void* return_addr) noexcept(S == CAPI) {
STAT_TIMER(t0, "us_timer_slowpath_getitem", 10);
// This possibly could just be represented as a single callattr; the only tricky part
......@@ -6372,60 +6481,33 @@ extern "C" Box* getitem(Box* target, Box* slice) {
static StatCounter slowpath_getitem("slowpath_getitem");
slowpath_getitem.log();
std::unique_ptr<Rewriter> rewriter(
Rewriter::createRewriter(__builtin_extract_return_addr(__builtin_return_address(0)), 2, "getitem"));
std::unique_ptr<Rewriter> rewriter(Rewriter::createRewriter(return_addr, 2, "getitem"));
Box* rtn;
if (rewriter.get()) {
GetitemRewriteArgs rewrite_args(rewriter.get(), rewriter->getArg(0), rewriter->getArg(1),
rewriter->getReturnDestination());
rtn = getitemInternal<CXX>(target, slice, &rewrite_args);
rtn = getitemInternal<S, REWRITABLE>(target, slice, &rewrite_args);
if (!rewrite_args.out_success) {
rewriter.reset(NULL);
} else {
} else if (rtn) {
rewriter->commitReturning(rewrite_args.out_rtn);
}
} else {
rtn = getitemInternal<CXX>(target, slice);
rtn = getitemInternal<S>(target, slice);
}
assert(rtn);
return rtn;
}
// target[slice]
extern "C" Box* getitem_capi(Box* target, Box* slice) noexcept {
STAT_TIMER(t0, "us_timer_slowpath_getitem", 10);
// This possibly could just be represented as a single callattr; the only tricky part
// are the error messages.
// Ex "(1)[1]" and "(1).__getitem__(1)" give different error messages.
static StatCounter slowpath_getitem("slowpath_getitem");
slowpath_getitem.log();
std::unique_ptr<Rewriter> rewriter(
Rewriter::createRewriter(__builtin_extract_return_addr(__builtin_return_address(0)), 2, "getitem"));
Box* rtn;
if (rewriter.get()) {
GetitemRewriteArgs rewrite_args(rewriter.get(), rewriter->getArg(0), rewriter->getArg(1),
rewriter->getReturnDestination());
rtn = getitemInternal<CAPI>(target, slice, &rewrite_args);
if (!rewrite_args.out_success) {
rewriter.reset(NULL);
} else if (rtn) {
rewriter->commitReturning(rewrite_args.out_rtn);
}
} else {
rtn = getitemInternal<CAPI>(target, slice);
}
extern "C" Box* getitem(Box* target, Box* slice) {
return getitemEntry<CXX>(target, slice, __builtin_extract_return_addr(__builtin_return_address(0)));
}
return rtn;
extern "C" Box* getitem_capi(Box* target, Box* slice) noexcept {
return getitemEntry<CAPI>(target, slice, __builtin_extract_return_addr(__builtin_return_address(0)));
}
// target[slice] = value
......@@ -6439,7 +6521,6 @@ extern "C" void setitem(Box* target, Box* slice, Box* value) {
Rewriter::createRewriter(__builtin_extract_return_addr(__builtin_return_address(0)), 3, "setitem"));
static BoxedString* setitem_str = getStaticString("__setitem__");
static BoxedString* setslice_str = getStaticString("__setslice__");
auto&& m = target->cls->tp_as_mapping;
if (m && m->mp_ass_subscript && m->mp_ass_subscript != slot_mp_ass_subscript) {
......@@ -6468,13 +6549,13 @@ extern "C" void setitem(Box* target, Box* slice, Box* value) {
rewrite_args.arg1 = rewriter->getArg(1);
rewrite_args.arg2 = rewriter->getArg(2);
rtn = callItemOrSliceAttr<CXX, REWRITABLE>(target, setitem_str, setslice_str, slice, value, &rewrite_args);
rtn = callItemAttr<CXX, REWRITABLE>(target, setitem_str, slice, value, &rewrite_args);
if (!rewrite_args.out_success) {
rewriter.reset(NULL);
}
} else {
rtn = callItemOrSliceAttr<CXX, NOT_REWRITABLE>(target, setitem_str, setslice_str, slice, value, NULL);
rtn = callItemAttr<CXX, NOT_REWRITABLE>(target, setitem_str, slice, value, NULL);
}
if (rtn == NULL) {
......@@ -6497,20 +6578,18 @@ extern "C" void delitem(Box* target, Box* slice) {
Rewriter::createRewriter(__builtin_extract_return_addr(__builtin_return_address(0)), 2, "delitem"));
static BoxedString* delitem_str = getStaticString("__delitem__");
static BoxedString* delslice_str = getStaticString("__delslice__");
Box* rtn;
if (rewriter.get()) {
CallRewriteArgs rewrite_args(rewriter.get(), rewriter->getArg(0), rewriter->getReturnDestination());
rewrite_args.arg1 = rewriter->getArg(1);
rtn = callItemOrSliceAttr<CXX, REWRITABLE>(target, delitem_str, delslice_str, slice, NULL, &rewrite_args);
rtn = callItemAttr<CXX, REWRITABLE>(target, delitem_str, slice, NULL, &rewrite_args);
if (!rewrite_args.out_success) {
rewriter.reset(NULL);
}
} else {
rtn = callItemOrSliceAttr<CXX, NOT_REWRITABLE>(target, delitem_str, delslice_str, slice, NULL, NULL);
rtn = callItemAttr<CXX, NOT_REWRITABLE>(target, delitem_str, slice, NULL, NULL);
}
if (rtn == NULL) {
......
......@@ -88,6 +88,9 @@ extern "C" Box* getitem_capi(Box* value, Box* slice) noexcept __attribute__((noi
extern "C" void setitem(Box* target, Box* slice, Box* value) __attribute__((noinline));
extern "C" void delitem(Box* target, Box* slice) __attribute__((noinline));
extern "C" PyObject* apply_slice(PyObject* u, PyObject* v, PyObject* w) noexcept;
extern "C" PyObject* applySlice(PyObject* u, PyObject* v, PyObject* w);
extern "C" int assign_slice(PyObject* u, PyObject* v, PyObject* w, PyObject* x) noexcept;
extern "C" void assignSlice(PyObject* u, PyObject* v, PyObject* w, PyObject* x);
extern "C" Box* getclsattr(Box* obj, BoxedString* attr) __attribute__((noinline));
extern "C" Box* getclsattrMaybeNonstring(Box* obj, Box* attr) __attribute__((noinline));
extern "C" Box* unaryop(Box* operand, int op_type) __attribute__((noinline));
......
......@@ -326,7 +326,7 @@ static PyTypeObject slots_tester_map= {
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
......
......@@ -242,3 +242,12 @@ class C(object):
c = C()
proxy = weakref.proxy(c)
print isinstance(proxy, C)
# test if __setslice__ is prioritised over mp_ass_subscript
class D(slots_test.SlotsTesterMap):
def __setslice__(self, *args):
print "setslice", args
d = D(1)
for i in xrange(10):
print i
d[1:2] = 1
......@@ -101,11 +101,13 @@ sliceable[3:8]
both[0]
both[:]
both[3:8]
both[slice(3, 8)]
both[::2] # this should call __getitem__ since __getslice__ doesn't support steps
both[0] = xrange(2)
both[:] = xrange(2)
both[3:8] = xrange(2)
both[slice(3, 8)] = xrange(2)
both[::2] = xrange(2)
# Should all call getitem as a fallback
......@@ -118,6 +120,7 @@ both[1:2:'c']
del both[0]
del both[:]
del both[3:8]
del both[slice(3, 8)]
del both [::2]
try:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment